diff --git a/core/modules/jsonld/jsonld.info b/core/modules/jsonld/jsonld.info
index 70d5d40..110b881 100644
--- a/core/modules/jsonld/jsonld.info
+++ b/core/modules/jsonld/jsonld.info
@@ -2,3 +2,4 @@ name = JSON-LD
 description = Serializes entities using JSON-LD format.
 package = Core
 core = 8.x
+dependencies[] = rdf
diff --git a/core/modules/jsonld/lib/Drupal/jsonld/JsonldBundle.php b/core/modules/jsonld/lib/Drupal/jsonld/JsonldBundle.php
index 538f4d0..00f1433 100644
--- a/core/modules/jsonld/lib/Drupal/jsonld/JsonldBundle.php
+++ b/core/modules/jsonld/lib/Drupal/jsonld/JsonldBundle.php
@@ -8,6 +8,7 @@
 namespace Drupal\jsonld;
 
 use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
 use Symfony\Component\HttpKernel\Bundle\Bundle;
 use Symfony\Component\Serializer\Serializer;
 
@@ -38,6 +39,10 @@ public function build(ContainerBuilder $container) {
       'entity' => array(
         'jsonld' => 'Drupal\jsonld\JsonldEntityNormalizer',
       ),
+      // RDF Schema.
+      'rdf_schema' => array(
+        'jsonld' => 'Drupal\jsonld\JsonldRdfSchemaNormalizer',
+      ),
     );
     // Encoders can only specify which format they support in
     // Encoder::supportsEncoding().
@@ -49,6 +54,8 @@ public function build(ContainerBuilder $container) {
     foreach ($normalizers as $supported_class => $formats) {
       foreach ($formats as $format => $normalizer_class) {
         $container->register("serializer.normalizer.{$supported_class}.{$format}", $normalizer_class)
+          ->addArgument(new Reference('rdf.site_schema_manager'))
+          ->addArgument(new Reference('rdf.mapping_manager'))
           ->addTag('normalizer', array('priority' => $priority));
       }
     }
diff --git a/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityNormalizer.php b/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityNormalizer.php
index 2ab909e..d84a446 100644
--- a/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityNormalizer.php
+++ b/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityNormalizer.php
@@ -8,6 +8,8 @@
 namespace Drupal\jsonld;
 
 use Drupal\jsonld\JsonldNormalizerBase;
+use Drupal\rdf\RdfMappingException;
+use Symfony\Component\Serializer\Exception\UnexpectedValueException;
 use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
 
 /**
@@ -26,7 +28,7 @@ class JsonldEntityNormalizer extends JsonldNormalizerBase implements Denormalize
    * Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize()
    */
   public function normalize($entity, $format = NULL) {
-    $entityWrapper = new JsonldEntityWrapper($entity, $format, $this->serializer);
+    $entityWrapper = new JsonldEntityWrapper($entity, $format, $this->serializer, $this->siteSchemaManager);
 
     $attributes = $entityWrapper->getProperties();
     $attributes = array(
@@ -38,24 +40,31 @@ public function normalize($entity, $format = NULL) {
 
   /**
    * Implements \Symfony\Component\Serializer\Normalizer\DenormalizerInterface::denormalize()
+   *
+   * @throws \Symfony\Component\Serializer\Exception\UnexpectedValueException
    */
   public function denormalize($data, $class, $format = null) {
     if (!isset($data['@type'])) {
-      // @todo Throw an exception?
+      throw new UnexpectedValueException('JSON-LD @type parameter must be included.');
     }
 
-    // Every bundle has a type, identified by URI. A schema which provides more
-    // information about the type can be requested from that URI. From that
-    // schema, the entity type and bundle name are extracted.
-    $typeUri = is_array($data['@type']) ? $data['@type'][0] : $data['@type'];
-    // @todo Instead of manually parsing the URI use an approach as discussed in
-    // http://drupal.org/node/1852812
-    $parts = explode('/', $typeUri);
-    $bundle = array_pop($parts);
-    $entity_type = array_pop($parts);
+    // Every bundle has a type, identified by URI. The incoming data should
+    // either include a type URI from this site's schema, or one of the type
+    // URIs in the incoming data must map to a site schema URI when passed
+    // through the RDF mapping manager.
+    $typeUris = is_array($data['@type']) ? $data['@type'] : array($data['@type']);
+    // If the RDF mapping manager can find a match to a site schema URI, it
+    // will return the corresponding Typed Data ids. Otherwise, throw an
+    // exception.
+    // @todo The @types might be CURIEs or aliases. Expand before trying to map.
+    try {
+      $typedDataIds = $this->rdfMappingManager->getTypedDataIdsFromTypeUris($typeUris);
+    } catch (RdfMappingException $e) {
+      throw new UnexpectedValueException($e->getMessage(), 0, $e);
+    }
 
     $values = array(
-      'type' => $bundle,
+      'type' => $typedDataIds['bundle'],
     );
     // If the data specifies a default language, use it to create the entity.
     if (isset($data['langcode'])) {
@@ -68,7 +77,7 @@ public function denormalize($data, $class, $format = null) {
     else if ($this->containsTranslation($data)) {
       $values['langcode'] = language(LANGUAGE_TYPE_CONTENT)->langcode;
     }
-    $entity = entity_create($entity_type, $values);
+    $entity = entity_create($typedDataIds['entity_type'], $values);
 
     // For each attribute in the JSON-LD, add the values as fields to the newly
     // created entity. It is assumed that the JSON attribute names are the same
diff --git a/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityReferenceNormalizer.php b/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityReferenceNormalizer.php
index e28ce95..723b90c 100644
--- a/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityReferenceNormalizer.php
+++ b/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityReferenceNormalizer.php
@@ -7,8 +7,10 @@
 
 namespace Drupal\jsonld;
 
+use Drupal\Core\Cache\DatabaseBackend;
 use Drupal\Core\Entity\EntityNG;
 use Drupal\jsonld\JsonldNormalizerBase;
+use Drupal\rdf\SiteSchema\SiteSchemaManager;
 use ReflectionClass;
 use Symfony\Component\Serializer\Exception\RuntimeException;
 use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
@@ -34,7 +36,7 @@ public function normalize($object, $format = NULL) {
     // of creating the array of properties, we could simply call normalize and
     // pass in the referenced entity with a flag that ensures it is rendered as
     // a node reference and not a node definition.
-    $entityWrapper = new JsonldEntityWrapper($object->entity, $format, $this->serializer);
+    $entityWrapper = new JsonldEntityWrapper($object->entity, $format, $this->serializer, $this->siteSchemaManager);
     return array(
       '@id' => $entityWrapper->getId(),
     );
diff --git a/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityWrapper.php b/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityWrapper.php
index cfffa8c..4a5864a 100644
--- a/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityWrapper.php
+++ b/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityWrapper.php
@@ -8,6 +8,9 @@
 namespace Drupal\jsonld;
 
 use Drupal\Core\Entity\Entity;
+use Drupal\rdf\SiteSchema\SiteSchema;
+use Drupal\rdf\SiteSchema\SiteSchemaManager;
+use Symfony\Component\Serializer\Serializer;
 
 /**
  * Provide an interface for JsonldNormalizer to get required properties.
@@ -43,6 +46,13 @@ class JsonldEntityWrapper {
   protected $serializer;
 
   /**
+   * The site schema manager.
+   *
+   * @var \Drupal\rdf\SiteSchema\SiteSchemaManager
+   */
+  protected $siteSchemaManager;
+
+  /**
    * Constructor.
    *
    * @param string $entity
@@ -51,11 +61,14 @@ class JsonldEntityWrapper {
    *   The format.
    * @param \Symfony\Component\Serializer\Serializer $serializer
    *   The serializer, provided by the SerializerAwareNormaizer.
+   * @param \Drupal\rdf\SiteSchema\SiteSchemaManager $siteSchemaManager
+   *   The site schema manager.
    */
-  public function __construct(Entity $entity, $format, $serializer) {
+  public function __construct(Entity $entity, $format, Serializer $serializer, SiteSchemaManager $siteSchemaManager) {
     $this->entity = $entity;
     $this->format = $format;
     $this->serializer = $serializer;
+    $this->siteSchemaManager = $siteSchemaManager;
   }
 
   /**
@@ -72,13 +85,21 @@ public function getId() {
   /**
    * Get the type URI.
    *
-   * @todo update or remove this method once the schema dependency to RDF module
-   * is sorted out.
+   * @todo Once RdfMappingManager has a mapOutputTypes event, use that instead
+   * of simply returning the site schema URI.
    */
   public function getTypeUri() {
     $entity_type = $this->entity->entityType();
     $bundle = $this->entity->bundle();
-    return url('site-schema/content-staging/' . $entity_type . '/' . $bundle, array('absolute' => TRUE));
+    switch ($this->format) {
+      case 'drupal_jsonld':
+        $schema_path = SiteSchema::CONTENT_DEPLOYMENT;
+        break;
+      case 'jsonld':
+        $schema_path = SiteSchema::SYNDICATION;
+    }
+    $schema = $this->siteSchemaManager->getSchema($schema_path);
+    return $schema->bundle($entity_type, $bundle)->getUri();
   }
 
   /**
diff --git a/core/modules/jsonld/lib/Drupal/jsonld/JsonldNormalizerBase.php b/core/modules/jsonld/lib/Drupal/jsonld/JsonldNormalizerBase.php
index bb9ae5a..b3774e0 100644
--- a/core/modules/jsonld/lib/Drupal/jsonld/JsonldNormalizerBase.php
+++ b/core/modules/jsonld/lib/Drupal/jsonld/JsonldNormalizerBase.php
@@ -8,6 +8,8 @@
 namespace Drupal\jsonld;
 
 use ReflectionClass;
+use Drupal\rdf\RdfMappingManager;
+use Drupal\rdf\SiteSchema\SiteSchemaManager;
 use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
 use Symfony\Component\Serializer\Normalizer\SerializerAwareNormalizer;
 
@@ -31,6 +33,33 @@
   static protected $format = array('jsonld', 'drupal_jsonld');
 
   /**
+   * The site schema manager.
+   *
+   * @var \Drupal\rdf\SiteSchema\SiteSchemaManager
+   */
+  protected $siteSchemaManager;
+
+  /**
+   * The RDF mapping manager.
+   *
+   * @var \Drupal\rdf\RdfMappingManager
+   */
+  protected $rdfMappingManager;
+
+  /**
+   * Constructor.
+   *
+   * @param \Drupal\rdf\SiteSchema\SiteSchemaManager $siteSchemaManager
+   *   The site schema manager.
+   * @param \Drupal\rdf\RdfMappingManager $rdfMappingManager
+   *   The RDF mapping manager.
+   */
+  public function __construct(SiteSchemaManager $siteSchemaManager, RdfMappingManager $rdfMappingManager) {
+    $this->siteSchemaManager = $siteSchemaManager;
+    $this->rdfMappingManager = $rdfMappingManager;
+  }
+
+  /**
    * Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize()
    */
   public function supportsNormalization($data, $format = NULL) {
diff --git a/core/modules/jsonld/lib/Drupal/jsonld/JsonldRdfSchemaNormalizer.php b/core/modules/jsonld/lib/Drupal/jsonld/JsonldRdfSchemaNormalizer.php
new file mode 100644
index 0000000..3505fc0
--- /dev/null
+++ b/core/modules/jsonld/lib/Drupal/jsonld/JsonldRdfSchemaNormalizer.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * @file
+ * Contains JsonldRdfSchemaNormalizer.
+ */
+
+namespace Drupal\jsonld;
+
+use Drupal\jsonld\JsonldNormalizerBase;
+use Drupal\rdf\RdfConstants;
+
+/**
+ * Converts the Drupal entity object structure to JSONLD array structure.
+ */
+class JsonldRdfSchemaNormalizer extends JsonldNormalizerBase {
+
+  /**
+     * The interface or class that this Normalizer supports.
+     *
+     * @var string
+     */
+  protected static $supportedInterfaceOrClass = 'Drupal\rdf\SiteSchema\SchemaTermBase';
+
+  /**
+     * Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize()
+     */
+  public function normalize($data, $format = NULL) {
+    $normalized = array();
+    $graph = $data->getGraph();
+
+    foreach ($graph as $termUri => $properties) {
+      // JSON-LD uses the @type keyword as a stand-in for rdf:type. Replace any
+      // use of rdf:type and move the type to the front of the property array.
+      if (isset($properties[RdfConstants::RDF_TYPE])) {
+        $properties = array(
+          '@type' => $properties[RdfConstants::RDF_TYPE],
+        ) + $properties;
+      }
+      unset($properties[RdfConstants::RDF_TYPE]);
+
+      // Add the @id keyword to the front of the array.
+      $normalized[] = array(
+        '@id' => $termUri,
+      ) + $properties;
+    }
+
+    return $normalized;
+  }
+
+}
diff --git a/core/modules/jsonld/lib/Drupal/jsonld/Tests/JsonldTestSetupHelper.php b/core/modules/jsonld/lib/Drupal/jsonld/Tests/JsonldTestSetupHelper.php
new file mode 100644
index 0000000..d0eb0c3
--- /dev/null
+++ b/core/modules/jsonld/lib/Drupal/jsonld/Tests/JsonldTestSetupHelper.php
@@ -0,0 +1,96 @@
+<?php
+
+/**
+ * @file
+ * Contains JsonldTestSetupHelper.
+ */
+
+namespace Drupal\jsonld\Tests;
+
+use Drupal\Core\Cache\DatabaseBackend;
+use Drupal\jsonld\JsonldEncoder;
+use Drupal\jsonld\JsonldEntityNormalizer;
+use Drupal\jsonld\JsonldEntityReferenceNormalizer;
+use Drupal\jsonld\JsonldFieldItemNormalizer;
+use Drupal\rdf\RdfMappingManager;
+use Drupal\rdf\EventSubscriber\MappingSubscriber;
+use Drupal\rdf\SiteSchema\SiteSchemaManager;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\Serializer\Serializer;
+
+/**
+ * Constructs services for JSON-LD tests.
+ */
+class JsonldTestSetupHelper {
+
+  /**
+   * The site schema manager.
+   *
+   * @var \Drupal\rdf\SiteSchema\SiteSchemaManager
+   */
+  protected $siteSchemaManager;
+
+  /**
+   * The RDF mapping manager.
+   *
+   * @var \Drupal\rdf\RdfMappingManager
+   */
+  protected $rdfMappingManager;
+
+  /**
+   * The Normalizer array.
+   *
+   * @var array
+   */
+  protected $normalizers;
+
+  /**
+   * Constructor.
+   */
+  public function __construct() {
+    // Construct site schema manager.
+    $this->siteSchemaManager = new SiteSchemaManager(new DatabaseBackend('cache'));
+    // Construct RDF mapping manager.
+    $dispatcher = new EventDispatcher();
+    $dispatcher->addSubscriber(new MappingSubscriber());
+    $this->rdfMappingManager = new RdfMappingManager($dispatcher, $this->siteSchemaManager);
+    // Construct normalizers.
+    $this->normalizers = array(
+      'entityreference' => new JsonldEntityReferenceNormalizer($this->siteSchemaManager, $this->rdfMappingManager),
+      'field_item' => new JsonldFieldItemNormalizer($this->siteSchemaManager, $this->rdfMappingManager),
+      'entity' => new JsonldEntityNormalizer($this->siteSchemaManager, $this->rdfMappingManager),
+    );
+    $serializer = new Serializer($this->normalizers, array(new JsonldEncoder()));
+    $this->normalizers['entity']->setSerializer($serializer);
+  }
+
+  /**
+   * Get Normalizers.
+   *
+   * @return array
+   *   An array of normalizers, keyed by supported class or interface.
+   */
+  public function getNormalizers() {
+    return $this->normalizers;
+  }
+
+  /**
+   * Get the SiteSchemaManager object.
+   *
+   * @return \Drupal\rdf\SiteSchema\SiteSchemaManager
+   *   The SiteSchemaManager, which is also injected into the Normalizers.
+   */
+  public function getSiteSchemaManager() {
+    return $this->siteSchemaManager;
+  }
+
+  /**
+   * Get the RdfMappingManager object.
+   *
+   * @return \Drupal\rdf\RdfMappingManager
+   *   The RdfMappingManager, which is also injected into the Normalizers.
+   */
+  public function getRdfMappingManager() {
+    return $this->rdfMappingManager;
+  }
+}
diff --git a/core/modules/jsonld/lib/Drupal/jsonld/Tests/NormalizeDenormalizeTest.php b/core/modules/jsonld/lib/Drupal/jsonld/Tests/NormalizeDenormalizeTest.php
index 9ecfda5..7a6fff2 100644
--- a/core/modules/jsonld/lib/Drupal/jsonld/Tests/NormalizeDenormalizeTest.php
+++ b/core/modules/jsonld/lib/Drupal/jsonld/Tests/NormalizeDenormalizeTest.php
@@ -8,15 +8,15 @@
 namespace Drupal\jsonld\Tests;
 
 use Drupal\Core\Language\Language;
-use Drupal\jsonld\JsonldEncoder;
-use Drupal\jsonld\JsonldEntityNormalizer;
-use Drupal\jsonld\JsonldEntityReferenceNormalizer;
-use Drupal\jsonld\JsonldFieldItemNormalizer;
+use Drupal\rdf\SiteSchema\SiteSchema;
 use Drupal\simpletest\WebTestBase;
-use Symfony\Component\Serializer\Serializer;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Symfony\Component\Serializer\Exception\UnexpectedValueException;
 
 /**
  * Test the vendor specific JSON-LD normalizer.
+ *
+ * This is implemented as a WebTest because it requires use of the Entity API.
  */
 class NormalizeDenormalizeTest extends WebTestBase {
 
@@ -25,7 +25,7 @@ class NormalizeDenormalizeTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('language', 'entity_test');
+  public static $modules = array('language', 'entity_test', 'rdf');
 
   /**
    * The format being tested.
@@ -51,13 +51,8 @@ public static function getInfo() {
   function setUp() {
     parent::setUp();
 
-    $this->normalizers = array(
-      'entityreference' => new JsonldEntityReferenceNormalizer(),
-      'field_item' => new JsonldFieldItemNormalizer(),
-      'entity' => new JsonldEntityNormalizer(),
-    );
-    $serializer = new Serializer($this->normalizers, array(new JsonldEncoder()));
-    $this->normalizers['entity']->setSerializer($serializer);
+    $setupHelper = new JsonldTestSetupHelper();
+    $this->normalizers = $setupHelper->getNormalizers();
 
     // Add German as a language.
     $language = new Language(array(
@@ -146,8 +141,10 @@ public function testNormalize() {
   }
 
   function testDenormalize() {
+    $schema = new SiteSchema(SiteSchema::CONTENT_DEPLOYMENT);
+    $bundleUri = $schema->bundle('entity_test', 'entity_test')->getUri();
     $incomingData = array(
-      '@type' => url('jsonld-test/content-staging/entity_test/entity_test', array('absolute' => TRUE)),
+      '@type' => $bundleUri,
       'name' => array(
         'en' => array(
           array(
@@ -170,17 +167,36 @@ function testDenormalize() {
       ),
     );
 
+    // Test valid request.
     $entity = $this->normalizers['entity']->denormalize($incomingData, 'Drupal\Core\Entity\EntityNG', static::$format);
     $this->assertEqual('entity_test', $entity->bundle(), "Denormalize creates entity with correct bundle.");
     $this->assertEqual($incomingData['name']['en'], $entity->get('name')->getValue(), "Translatable field denormalized correctly in default language.");
     $this->assertEqual($incomingData['name']['de'], $entity->getTranslation('de')->get('name')->getValue(), "Translatable field denormalized correctly in translation language.");
     $this->assertEqual($incomingData['field_test_text']['und'], $entity->get('field_test_text')->getValue(), "Untranslatable field denormalized correctly.");
+
+    // Test request without @type.
+    unset($incomingData['@type']);
+    try {
+      $this->normalizers['entity']->denormalize($incomingData, 'Drupal\Core\Entity\EntityNG', static::$format);
+      $this->fail('Trying to denormalize entity data without @type results in exception.');
+    } catch (UnexpectedValueException $e) {
+      $this->pass('Trying to denormalize entity data without @type results in exception.');
+    }
+
+    // Test request with @type that has no valid mapping.
+    $incomingData['@type'] = 'http://failing-uri.com/type';
+    try {
+      $this->normalizers['entity']->denormalize($incomingData, 'Drupal\Core\Entity\EntityNG', static::$format);
+      $this->fail('Trying to denormalize entity data with unrecognized @type results in exception.');
+    } catch (UnexpectedValueException $e) {
+      $this->pass('Trying to denormalize entity data with unrecognized @type results in exception.');
+    }
   }
 
   /**
    * Get the Entity ID.
    *
-   * @param Drupal\Core\Entity\EntityNG $entity
+   * @param \Drupal\Core\Entity\EntityNG $entity
    *   Entity to get URI for.
    *
    * @return string
diff --git a/core/modules/jsonld/lib/Drupal/jsonld/Tests/RdfSchemaSerializationTest.php b/core/modules/jsonld/lib/Drupal/jsonld/Tests/RdfSchemaSerializationTest.php
new file mode 100644
index 0000000..27a8d79
--- /dev/null
+++ b/core/modules/jsonld/lib/Drupal/jsonld/Tests/RdfSchemaSerializationTest.php
@@ -0,0 +1,55 @@
+<?php
+/**
+ * @file
+ * Contains RdfSchemaSerializationTest.
+ */
+
+namespace Drupal\jsonld\Tests;
+
+use Drupal\jsonld\JsonldRdfSchemaNormalizer;
+use Drupal\jsonld\JsonldEncoder;
+use Drupal\rdf\SiteSchema\BundleSchema;
+use Drupal\rdf\SiteSchema\SiteSchema;
+use Drupal\simpletest\DrupalUnitTestBase;
+use Symfony\Component\Serializer\Serializer;
+
+class RdfSchemaSerializationTest extends DrupalUnitTestBase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Site schema JSON-LD serialization',
+      'description' => 'Tests the JSON-LD serialization of the RDF site schema.',
+      'group' => 'JSON-LD',
+    );
+  }
+
+  /**
+   * Tests the serialization of site schemas.
+   */
+  function testSchemaSerialization() {
+    // In order to use url() the url_alias table must be installed, so system
+    // is enabled.
+    $this->enableModules(array('system'));
+
+    $entityType = $bundle = 'entity_test';
+
+    // Set up the bundle schema for the entity_test bundle.
+    $schema = new SiteSchema(SiteSchema::CONTENT_DEPLOYMENT);
+    $bundleSchema = $schema->bundle($entityType, $bundle);
+    // Set up the serializer.
+    $setupHelper = new JsonldTestSetupHelper();
+    $normalizer = new JsonldRdfSchemaNormalizer($setupHelper->getSiteSchemaManager(), $setupHelper->getRdfMappingManager());
+    $serializer = new Serializer(array($normalizer), array(new JsonldEncoder()));
+
+    $serialized = $serializer->serialize($bundleSchema, 'jsonld');
+    $decoded = json_decode($serialized);
+    $parsedTerm = $decoded[0];
+
+    $this->assertEqual($parsedTerm->{'@id'}, $bundleSchema->getUri(), 'JSON-LD for schema term uses correct @id.');
+    $this->assertEqual($parsedTerm->{'@type'}, 'http://www.w3.org/2000/01/rdf-schema#class', 'JSON-LD for schema term uses correct @type.');
+    // The @id and @type should be placed in the beginning of the array.
+    $arrayKeys = array_keys((array) $parsedTerm);
+    $this->assertEqual(array('@id', '@type'), array_slice($arrayKeys, 0, 2), 'JSON-LD keywords are placed before other properties.');
+    $this->assertTrue(isset($parsedTerm->{'http://www.w3.org/2000/01/rdf-schema#isDefinedBy'}), 'Other properties of the term are included.');
+  }
+}
diff --git a/core/modules/jsonld/lib/Drupal/jsonld/Tests/SupportsSerializationTest.php b/core/modules/jsonld/lib/Drupal/jsonld/Tests/SupportsSerializationTest.php
index b0d4474..301e851 100644
--- a/core/modules/jsonld/lib/Drupal/jsonld/Tests/SupportsSerializationTest.php
+++ b/core/modules/jsonld/lib/Drupal/jsonld/Tests/SupportsSerializationTest.php
@@ -8,11 +8,7 @@
 namespace Drupal\jsonld\Tests;
 
 use Drupal\config\Tests\ConfigEntityTest;
-use Drupal\jsonld\JsonldEntityNormalizer;
-use Drupal\jsonld\JsonldEntityReferenceNormalizer;
-use Drupal\jsonld\JsonldFieldItemNormalizer;
 use Drupal\simpletest\WebTestBase;
-use Symfony\Component\Serializer\Serializer;
 
 /**
  * Test the vendor specific JSON-LD normalizer.
@@ -50,13 +46,8 @@ public static function getInfo() {
   function setUp() {
     parent::setUp();
 
-    $this->normalizers = array(
-      'entityreference' => new JsonldEntityReferenceNormalizer(),
-      'field_item' => new JsonldFieldItemNormalizer(),
-      'entity' => new JsonldEntityNormalizer(),
-    );
-    $serializer = new Serializer($this->normalizers);
-    $this->normalizers['entity']->setSerializer($serializer);
+    $setupHelper = new JsonldTestSetupHelper();
+    $this->normalizers = $setupHelper->getNormalizers();
   }
 
   /**
diff --git a/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/MappingSubscriber.php b/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/MappingSubscriber.php
new file mode 100644
index 0000000..6f663b5
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/MappingSubscriber.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * @file
+ * Contains MappingSubscriber.
+ */
+
+namespace Drupal\rdf\EventSubscriber;
+
+use Drupal\rdf\RdfMappingEvents;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Default RDF mapping handling.
+ */
+class MappingSubscriber implements EventSubscriberInterface {
+
+  /**
+   * Stops event if incoming URI is a site schema URI.
+   *
+   * If the incoming URI is one of the site's own registered types, then
+   * mapping is unnecessary. Mapping is only necessary if the incoming type URI
+   * is from an external vocabulary.
+   *
+   * @param \Drupal\rdf\MapTypesFromInputEvent $event
+   */
+  public function mapTypesFromInput(\Drupal\rdf\MapTypesFromInputEvent $event) {
+    $inputUris = $event->getInputUris();
+    $siteSchemaTypes = $event->getSiteSchemaTypes();
+    foreach ($inputUris as $inputUri) {
+      if (isset($siteSchemaTypes[$inputUri])) {
+        $event->setSiteSchemaUri($inputUri);
+        $event->stopPropagation();
+      }
+    }
+  }
+
+  /**
+   * Implements EventSubscriberInterface::getSubscribedEvents().
+   */
+  static function getSubscribedEvents() {
+    $events[RdfMappingEvents::MAP_TYPES_FROM_INPUT] = 'mapTypesFromInput';
+    return $events;
+  }
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/RouteSubscriber.php b/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/RouteSubscriber.php
new file mode 100644
index 0000000..0c6d60a
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/RouteSubscriber.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * @file
+ * Contains RouteSubscriber.
+ */
+
+namespace Drupal\rdf\EventSubscriber;
+
+use Drupal\Core\Routing\RouteBuildEvent;
+use Drupal\Core\Routing\RoutingEvents;
+use Drupal\rdf\SiteSchema\SiteSchema;
+use Drupal\rdf\SiteSchema\SiteSchemaManager;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Subscriber for site-generated schema routes.
+ */
+class RouteSubscriber implements EventSubscriberInterface {
+
+  /**
+   * The site schema manager.
+   *
+   * @var \Drupal\rdf\SiteSchema\SiteSchemaManager
+   */
+  protected $siteSchemaManager;
+
+  /**
+   * Constructor.
+   *
+   * @param \Drupal\rdf\SiteSchema\SiteSchemaManager $siteSchemaManager
+   *   The injected site schema manager.
+   */
+  public function __construct(SiteSchemaManager $siteSchemaManager) {
+    $this->siteSchemaManager = $siteSchemaManager;
+  }
+
+  /**
+   * Adds routes for term types in the site-generated schemas.
+   *
+   * @param \Drupal\Core\Routing\RouteBuildEvent $event
+   *   The route building event.
+   */
+  public function routes(RouteBuildEvent $event) {
+
+    $collection = $event->getRouteCollection();
+
+    // Add the routes for all of the terms in both schemas.
+    foreach ($this->siteSchemaManager->getSchemas() as $schema) {
+      $routes = $schema->getRoutes();
+      foreach ($routes as $controller => $pattern) {
+        $schemaPath = $schema->getPath();
+        $route = new Route($pattern, array(
+          '_controller' => 'Drupal\rdf\SiteSchema\SchemaController::' . $controller,
+          'schema_path' => $schemaPath,
+        ), array(
+          '_method' => 'GET',
+          '_access' => 'TRUE',
+        ));
+        // Create the route name to use in the RouteCollection. Remove the
+        // trailing slash and replace characters, so that a path such as
+        // site-schema/syndication/ becomes rdf.site_schema.syndication.
+        $routeName = 'rdf.' . str_replace(array('-','/'), array('_', '.'), substr_replace($schemaPath ,"",-1));
+        $collection->add($routeName, $route);
+      }
+    }
+  }
+
+  /**
+   * Implements EventSubscriberInterface::getSubscribedEvents().
+   */
+  static function getSubscribedEvents() {
+    $events[RoutingEvents::DYNAMIC] = 'routes';
+    return $events;
+  }
+}
+
diff --git a/core/modules/rdf/lib/Drupal/rdf/MapTypesFromInputEvent.php b/core/modules/rdf/lib/Drupal/rdf/MapTypesFromInputEvent.php
new file mode 100644
index 0000000..6b0e841
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/MapTypesFromInputEvent.php
@@ -0,0 +1,91 @@
+<?php
+
+/**
+ * @file
+ * Contains MapInputTypesEvent.
+ */
+
+namespace Drupal\rdf;
+
+use Symfony\Component\EventDispatcher\Event;
+
+/**
+ * Represents type mapping as event.
+ */
+class MapTypesFromInputEvent extends Event {
+
+  /**
+   * An array of incoming RDF type URIs.
+   *
+   * @var array
+   */
+  protected $inputUris;
+
+  /**
+   * An array of entity_type/bundles, keyed by site schema type URI
+   *
+   * @var array
+   */
+  protected $siteSchemaTypes;
+
+  /**
+   * The site schema type URI.
+   *
+   * @var string
+   */
+  protected $siteSchemaUri;
+
+  /**
+   * Constructor.
+   *
+   * @param $inputUris
+   *   An array of incoming RDF type URIs.
+   * @param $siteSchemaTypes
+   *   An array of entity_type/bundles, keyed by site schema type URI.
+   */
+  public function __construct($inputUris, $siteSchemaTypes) {
+    $this->inputUris = $inputUris;
+    $this->siteSchemaTypes = $siteSchemaTypes;
+    $this->siteSchemaUri = FALSE;
+  }
+
+  /**
+   * Gets the input URI.
+   *
+   * @return array
+   *   The array of incoming RDF type URIs.
+   */
+  public function getInputUris() {
+    return $this->inputUris;
+  }
+
+  /**
+   * Gets the cache of internal site schema types.
+   *
+   * @return array
+   *   The cached site schema type array.
+   */
+  public function getSiteSchemaTypes() {
+    return $this->siteSchemaTypes;
+  }
+
+  /**
+   * Gets the site schema URI.
+   *
+   * @return string|bool
+   *   The site schema type URI if set, FALSE if otherwise.
+   */
+  public function getSiteSchemaUri() {
+    return $this->siteSchemaUri;
+  }
+
+  /**
+   * Sets the site schema URI.
+   *
+   * @param string $uri
+   *   The site schema type URI.
+   */
+  public function setSiteSchemaUri($uri) {
+    $this->siteSchemaUri = $uri;
+  }
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/RdfBundle.php b/core/modules/rdf/lib/Drupal/rdf/RdfBundle.php
new file mode 100644
index 0000000..258ab6d
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/RdfBundle.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * @file
+ * Contains RdfBundle.
+ */
+
+namespace Drupal\rdf;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+/**
+ * RDF dependency injection container.
+ */
+class RdfBundle extends Bundle {
+
+  /**
+   * Overrides Symfony\Component\HttpKernel\Bundle\Bundle::build().
+   */
+  public function build(ContainerBuilder $container) {
+    // Site schema type cache.
+    $container->register('cache.rdf.site_schema.types', 'Drupal\Core\Cache\CacheBackendInterface')
+      ->setFactoryClass('Drupal\Core\Cache\CacheFactory')
+      ->setFactoryMethod('get')
+      ->addArgument('cache');
+
+    // Site schema manager service.
+    $container->register('rdf.site_schema_manager', 'Drupal\rdf\SiteSchema\SiteSchemaManager')
+      ->addArgument(new Reference('cache.rdf.site_schema.types'));
+    // Mapping manager service.
+    $container->register('rdf.mapping_manager', 'Drupal\rdf\RdfMappingManager')
+      ->addArgument(new Reference('dispatcher'))
+      ->addArgument(new Reference('rdf.site_schema_manager'));
+
+    // Mapping subscriber.
+    $container->register('rdf.mapping', 'Drupal\rdf\EventSubscriber\MappingSubscriber')
+      ->addTag('event_subscriber');
+    // Route subscriber.
+    $container->register('rdf.route_subscriber', 'Drupal\rdf\EventSubscriber\RouteSubscriber')
+      ->addArgument(new Reference('rdf.site_schema_manager'))
+      ->addTag('event_subscriber');
+  }
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/RdfConstants.php b/core/modules/rdf/lib/Drupal/rdf/RdfConstants.php
new file mode 100644
index 0000000..b64ae91
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/RdfConstants.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Contains RdfConstants.
+ */
+
+namespace Drupal\rdf;
+
+/**
+ * Defines constants for RDF terms.
+ */
+abstract class RdfConstants {
+  const RDF_TYPE            = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type';
+  // RDF Schema terms.
+  const RDFS_CLASS          = 'http://www.w3.org/2000/01/rdf-schema#class';
+  const RDFS_DOMAIN         = 'http://www.w3.org/2000/01/rdf-schema#domain';
+  const RDFS_IS_DEFINED_BY  = 'http://www.w3.org/2000/01/rdf-schema#isDefinedBy';
+  const RDFS_RANGE          = 'http://www.w3.org/2000/01/rdf-schema#range';
+  const RDFS_SUB_CLASS_OF   = 'http://www.w3.org/2000/01/rdf-schema#subClassOf';
+  // XSD datatypes.
+  const XSD_INTEGER         = 'http://www.w3.org/2001/XMLSchema#integer';
+  const XSD_DOUBLE          = 'http://www.w3.org/2001/XMLSchema#double';
+  const XSD_BOOLEAN         = 'http://www.w3.org/2001/XMLSchema#boolean';
+  const XSD_STRING          = 'http://www.w3.org/2001/XMLSchema#string';
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/RdfMappingEvents.php b/core/modules/rdf/lib/Drupal/rdf/RdfMappingEvents.php
new file mode 100644
index 0000000..0e3fdae
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/RdfMappingEvents.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * @file
+ * Contains RdfMappingEvents.
+ */
+
+namespace Drupal\rdf;
+
+/**
+ * Contains all events for mapping site schemas to external vocabularies.
+ */
+final class RdfMappingEvents {
+
+  /**
+   * Maps an array of incoming type URIs to a site schema URI.
+   *
+   * Modules can use this event to convert an RDF type from an externally
+   * defined vocabulary to a URI defined in the site's schema. From the site
+   * schema URI, the site can derive the Typed Data API ids, which can be used
+   * to create an entity.
+   *
+   * @see \Drupal\rdf\RdfMappingManager
+   *
+   * @var string
+   */
+  const MAP_TYPES_FROM_INPUT = 'rdf.map_types_from_input';
+
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/RdfMappingException.php b/core/modules/rdf/lib/Drupal/rdf/RdfMappingException.php
new file mode 100644
index 0000000..d96722f
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/RdfMappingException.php
@@ -0,0 +1,13 @@
+<?php
+
+/**
+ * @file
+ * Contains RdfMappingException.
+ */
+
+namespace Drupal\rdf;
+
+/**
+ * Exception to use when no RDF mapping is found.
+ */
+class RdfMappingException extends \Exception { }
diff --git a/core/modules/rdf/lib/Drupal/rdf/RdfMappingManager.php b/core/modules/rdf/lib/Drupal/rdf/RdfMappingManager.php
new file mode 100644
index 0000000..c34cdd7
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/RdfMappingManager.php
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * @file
+ * Contains RdfMappingManager.
+ */
+
+namespace Drupal\rdf;
+
+use ReflectionClass;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\rdf\MapTypesFromInputEvent;
+use Drupal\rdf\RdfMappingEvents;
+use Drupal\rdf\SiteSchema\BundleSchema;
+use Drupal\rdf\SiteSchema\SiteSchema;
+use Drupal\rdf\SiteSchema\SiteSchemaManager;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+
+/**
+ * Manager for mapping internal and external schema terms.
+ */
+class RdfMappingManager {
+
+  /**
+   * The event dispatcher.
+   *
+   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+   */
+  protected $dispatcher;
+
+  /**
+   * The site schema manager.
+   *
+   * @var \Drupal\rdf\SiteSchema\SiteSchemaManager
+   */
+  protected $siteSchemaManager;
+
+  /**
+   * Constructor.
+   *
+   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
+   *   The event dispatcher.
+   * @param \Drupal\rdf\SiteSchema\SiteSchemaManager $siteSchemaManager
+   *   The site schema manager.
+   */
+  public function __construct(EventDispatcherInterface $dispatcher, SiteSchemaManager $siteSchemaManager) {
+    $this->dispatcher = $dispatcher;
+    $this->siteSchemaManager = $siteSchemaManager;
+  }
+
+  /**
+   * Convert an array of RDF type URIs to the corresponding TypedData IDs.
+   *
+   * @param array $inputRdfTypes
+   *   An array of URIs for the type.
+   *
+   * @return array
+   *   An array containing entity_type and bundle.
+   *
+   * @throws \Drupal\rdf\RdfMappingException
+   */
+  public function getTypedDataIdsFromTypeUris($inputRdfTypes) {
+    // Get the cache of site schema types.
+    $siteSchemaTypes = $this->siteSchemaManager->getTypes();
+    // Map the RDF type from the incoming data to an RDF type defined in the
+    // internal site schema.
+    $typeUri = $this->mapTypesFromInput($inputRdfTypes);
+    // If no site schema URI has been determined, then it's impossible to know
+    // what entity type to create. Throw an exception.
+    if ($typeUri == FALSE) {
+      throw new RdfMappingException(sprintf('No mapping to a site schema type URI found for incoming types (%s).', implode(',', $inputRdfTypes)));
+    }
+    // Use the mapped RDF type URI to get the TypedData API ids the rest of the
+    // system uses (entity type and bundle).
+    return $siteSchemaTypes[$typeUri];
+  }
+
+  /**
+   * Map an array of incoming URIs to an internal site schema URI.
+   *
+   * @param array $inputRdfTypes
+   *   An array of RDF type URIs.
+   *
+   * @return string
+   *   The corresponding site schema type URI.
+   */
+  protected function mapTypesFromInput($inputRdfTypes) {
+    // Create the event using the array of incoming RDF type URIs and the cache
+    // of internal site schema URIs.
+    $siteSchemaTypes = $this->siteSchemaManager->getTypes();
+    $mapping_event = new MapTypesFromInputEvent($inputRdfTypes, $siteSchemaTypes);
+
+    // Allow other modules to map the incoming type URIs to an internal site
+    // schema type URI. For example, a content deployment module could take
+    // URIs from the staging site's schema and map them to the corresponding
+    // URI in the live site's schema.
+    $this->dispatcher->dispatch(RdfMappingEvents::MAP_TYPES_FROM_INPUT, $mapping_event);
+
+    return $mapping_event->getSiteSchemaUri();
+  }
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/BundleSchema.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/BundleSchema.php
new file mode 100644
index 0000000..7c9196d
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/BundleSchema.php
@@ -0,0 +1,64 @@
+<?php
+
+/**
+ * @file
+ * Contains BundleSchema.
+ */
+
+namespace Drupal\rdf\SiteSchema;
+
+use Drupal\rdf\RdfConstants;
+use Drupal\rdf\SiteSchema\EntitySchema;
+
+/**
+ * Defines RDF terms corresponding to Drupal bundles.
+ */
+class BundleSchema extends EntitySchema {
+
+  /**
+   * The URI pattern for bundle site schema terms.
+   *
+   * @var string
+   */
+  public static $uriPattern = '{entity_type}/{bundle}';
+
+  /**
+   * The bundle that this term identifies.
+   *
+   * @var string
+   */
+  protected $bundle;
+
+  /**
+   * Constructor.
+   *
+   * @param \Drupal\rdf\SiteSchema\SiteSchema $siteSchema
+   *   The schema the term is defined in.
+   * @param string $entity_type
+   *   The entity type.
+   * @param string $bundle
+   *   The bundle.
+   */
+  public function __construct($siteSchema, $entity_type, $bundle) {
+    parent::__construct($siteSchema, $entity_type);
+    $this->bundle = $bundle;
+  }
+
+  /**
+   * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getUri().
+   */
+  public function getUri() {
+    $path = str_replace(array('{entity_type}', '{bundle}'), array($this->entityType, $this->bundle), static::$uriPattern);
+    return $this->siteSchema->getUri() . $path;
+  }
+
+  /**
+   * Overrides \Drupal\rdf\SiteSchema\SchemaTermBase::getProperties().
+   */
+  public function getProperties() {
+    $properties = parent::getProperties();
+    $properties[RdfConstants::RDFS_SUB_CLASS_OF] = $this->siteSchema->entity($this->entityType)->getUri();
+    return $properties;
+  }
+
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/EntitySchema.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/EntitySchema.php
new file mode 100644
index 0000000..ec72219
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/EntitySchema.php
@@ -0,0 +1,73 @@
+<?php
+
+/**
+ * @file
+ * Contains EntitySchema.
+ */
+
+namespace Drupal\rdf\SiteSchema;
+
+use Drupal\rdf\RdfConstants;
+use Drupal\rdf\SiteSchema\SchemaTermBase;
+
+/**
+ * Defines RDF terms corresponding to Drupal entity types.
+ */
+class EntitySchema extends SchemaTermBase {
+
+  /**
+   * The URI pattern for entity site schema terms.
+   *
+   * @var string
+   */
+  public static $uriPattern = '{entity_type}';
+
+  /**
+   * The entity type that this term identifies.
+   *
+   * @var string
+   */
+  protected $entityType;
+
+  /**
+   * Constructor.
+   *
+   * @param \Drupal\rdf\SiteSchema\SiteSchema $siteSchema
+   *   The schema the term is defined in.
+   * @param string $entity_type
+   *   The entity type.
+   */
+  public function __construct($siteSchema, $entity_type) {
+    parent::__construct($siteSchema);
+    $this->entityType = $entity_type;
+  }
+
+  /**
+   * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getGraph().
+   *
+   * @todo Loop through all fields and add their RDF descriptions.
+   */
+  public function getGraph() {
+    $graph = array();
+    $graph[$this->getUri()] = $this->getProperties();
+    return $graph;
+  }
+
+  /**
+   * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getUri().
+   */
+  public function getUri() {
+    $path = str_replace('{entity_type}', $this->entityType , static::$uriPattern);
+    return $this->siteSchema->getUri() . $path;
+  }
+
+  /**
+   * Overrides \Drupal\rdf\SiteSchema\SchemaTermBase::getProperties().
+   */
+  public function getProperties() {
+    $properties = parent::getProperties();
+    $properties[RdfConstants::RDF_TYPE] = RdfConstants::RDFS_CLASS;
+    return $properties;
+  }
+
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaController.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaController.php
new file mode 100644
index 0000000..93abb9d
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaController.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Contains SchemaController.
+ */
+
+namespace Drupal\rdf\SiteSchema;
+
+use Drupal\rdf\SiteSchema\SiteSchema;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\DependencyInjection\ContainerAwareInterface;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+
+/**
+ * Resource controller for displaying entity schemas.
+ */
+class SchemaController implements ContainerAwareInterface {
+
+  /**
+   * The injection container for this object.
+   *
+   * @var \Symfony\Component\DependencyInjection\ContainerInterface
+   */
+  protected $container;
+
+  /**
+   * Injects the service container used by this object.
+   *
+   * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
+   *   The service container this object should use.
+   */
+  public function setContainer(ContainerInterface $container = NULL) {
+    $this->container = $container;
+  }
+
+  /**
+   * Responds to a schema request for a bundle of a given entity type.
+   *
+   * @param string $entity_type
+   *   The entity type.
+   * @param string $bundle
+   *   The entity bundle.
+   * @param string $schema_path
+   *   The relative base path for the schema.
+   *
+   * @return \Symfony\Component\HttpFoundation\Response
+   *   The response object.
+   *
+   * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
+   */
+  public function bundle($entity_type, $bundle, $schema_path) {
+    if (!$entity_info = entity_get_info($entity_type)) {
+      throw new NotFoundHttpException(t('Entity type @entity_type not found', array('@entity_type' => $entity_type)));
+    }
+    if (!array_key_exists($bundle, $entity_info['bundles'])) {
+      throw new NotFoundHttpException(t('Bundle @bundle not found', array('@bundle' => $bundle)));
+    }
+
+    $serializer = $this->container->get('serializer');
+    $siteSchemaManager = $this->container->get('rdf.site_schema_manager');
+    $schema = $siteSchemaManager->getSchema($schema_path);
+    // @todo Remove hard-coded mimetype once we have proper conneg.
+    $content = $serializer->serialize($schema->bundle($entity_type, $bundle), 'jsonld');
+    return new Response($content, 200, array('Content-type' => 'application/json'));
+  }
+
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermBase.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermBase.php
new file mode 100644
index 0000000..abff580
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermBase.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * @file
+ * Contains SchemaTermBase.
+ */
+
+namespace Drupal\rdf\SiteSchema;
+
+use Drupal\rdf\RdfConstants;
+
+/**
+ * Base class to define an RDF term in a schema.
+ */
+abstract class SchemaTermBase implements SchemaTermInterface {
+
+  /**
+   * The URI pattern for this type of site schema term.
+   *
+   * @var string
+   */
+  public static $uriPattern;
+
+  /**
+   * The schema in which this term is defined.
+   *
+   * @var \Drupal\rdf\SiteSchema\SiteSchema
+   */
+  protected $siteSchema;
+
+  /**
+   * Constructor.
+   *
+   * @param \Drupal\rdf\SiteSchema\SiteSchema $siteSchema
+   *   The namespace.
+   */
+  public function __construct($siteSchema) {
+    $this->siteSchema = $siteSchema;
+  }
+
+  /**
+   * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getProperties().
+   */
+  public function getProperties() {
+    return array(
+      RdfConstants::RDFS_IS_DEFINED_BY => $this->siteSchema->getUri(),
+    );
+  }
+
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermInterface.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermInterface.php
new file mode 100644
index 0000000..342b51e
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermInterface.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * @file
+ * Contains SchemaTermInterface.
+ */
+
+namespace Drupal\rdf\SiteSchema;
+
+interface SchemaTermInterface {
+
+  /**
+   * Get the full graph of terms and properties to display.
+   *
+   * When an RDF term URI is dereferenced, it usually contains a description of
+   * the term in RDF. To make it easier to use this description, include
+   * information about all related terms. For example, when viewing the RDF
+   * description for the RDF class which corresponds to a Drupal bundle, data
+   * about its fields would also be included.
+   *
+   * @return array
+   *   An array of terms and their properties, keyed by term URI.
+   */
+  public function getGraph();
+
+  /**
+   * Get the term properties.
+   *
+   * @return array
+   *   An array of properties for this term, keyed by URI.
+   */
+  public function getProperties();
+
+  /**
+   * Get the URI of the term.
+   *
+   * Implementations of this method will use the URI patterns defined in
+   * $uriPattern static variables and replace placeholders with actual values.
+   *
+   * @return string
+   *   The URI of the term.
+   */
+  public function getUri();
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchema.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchema.php
new file mode 100644
index 0000000..0bdf5d9
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchema.php
@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * @file
+ * Contains SiteSchema.
+ */
+
+namespace Drupal\rdf\SiteSchema;
+
+use Drupal\rdf\SiteSchema\BundleSchema;
+use Drupal\rdf\SiteSchema\EntitySchema;
+
+/**
+ * Defines a site-generated schema.
+ */
+class SiteSchema {
+
+  // Site schema paths. There are only two site schemas provided by core, which
+  // are not intended to be extensible. If a site wants to use external
+  // vocabulary terms, the appropriate way to do this is to use the RDF mapping
+  // system.
+  const CONTENT_DEPLOYMENT = 'site-schema/content-deployment/';
+  const SYNDICATION     = 'site-schema/syndication/';
+
+  /**
+   * The relative base path of the instantiated schema.
+   *
+   * @var string
+   */
+  protected $schemaPath;
+
+  /**
+   * Constructor.
+   *
+   * @param string $schemaPath
+   */
+  public function __construct($schemaPath) {
+    $this->schemaPath = $schemaPath;
+  }
+
+  /**
+   * Get an entity's term definition in this vocabulary.
+   */
+  public function entity($entity_type) {
+    return new EntitySchema($this, $entity_type);
+  }
+
+  /**
+   * Get a bundle's term definition in this vocabulary.
+   */
+  public function bundle($entity_type, $bundle) {
+    return new BundleSchema($this, $entity_type, $bundle);
+  }
+
+  /**
+   * Get the URI of the schema.
+   *
+   * @return string
+   *   The URI of the schema.
+   */
+  public function getUri() {
+    return url($this->schemaPath, array('absolute' => TRUE));
+  }
+
+  /**
+   * Get the relative base path of the schema.
+   */
+  public function getPath() {
+    return $this->schemaPath;
+  }
+
+  /**
+   * Get the routes for the types of terms defined in this schema.
+   *
+   * @return array
+   *   An array of route patterns, keyed by controller method name.
+   */
+  public function getRoutes() {
+    return array(
+      'bundle' => $this->schemaPath . BundleSchema::$uriPattern,
+    );
+  }
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchemaManager.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchemaManager.php
new file mode 100644
index 0000000..639b221
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchemaManager.php
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * @file
+ * Contains SiteSchemaManager.
+ */
+
+namespace Drupal\rdf\SiteSchema;
+
+use ReflectionClass;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\rdf\SiteSchema\SiteSchema;
+use Drupal\rdf\SiteSchema\BundleSchema;
+
+class SiteSchemaManager {
+
+  /**
+   * @var \Drupal\Core\Cache\CacheBackendInterface;
+   */
+  protected $cache;
+
+  /**
+   * Constructor.
+   */
+  public function __construct(CacheBackendInterface $cache) {
+    $this->cache = $cache;
+    $this->siteSchemas = array(
+      SiteSchema::CONTENT_DEPLOYMENT => new SiteSchema(SiteSchema::CONTENT_DEPLOYMENT),
+      SiteSchema::SYNDICATION => new SiteSchema(SiteSchema::SYNDICATION),
+    );
+  }
+
+  /**
+   * Writes the cache of site schema types.
+   */
+  public function writeCache() {
+    $data = array();
+
+    // Type URIs correspond to bundles. Iterate through the bundles to get the
+    // URI and data for them.
+    foreach (entity_get_info() as $entityType => $entityInfo) {
+      // Only content entities are supported currently.
+      // @todo Consider supporting config entities.
+      $reflection = new ReflectionClass($entityInfo['class']);
+      if ($reflection->implementsInterface('\Drupal\Core\Config\Entity\ConfigEntityInterface')) {
+        continue;
+      }
+      foreach ($entityInfo['bundles'] as $bundle => $bundleInfo) {
+        // Get a type URI for the bundle in each of the defined schemas.
+        foreach ($this->siteSchemas as $schema) {
+          $bundleUri = $schema->bundle($entityType, $bundle)->getUri();
+          $data[$bundleUri] = array(
+            'entity_type' => $entityType,
+            'bundle' => $bundle,
+          );
+        }
+      }
+    }
+    // These URIs only change when entity info changes, so cache it permanently
+    // and only clear it when entity_info is cleared.
+    $this->cache->set('rdf:site_schema:types', $data, CacheBackendInterface::CACHE_PERMANENT, array('entity_info' => TRUE));
+  }
+
+  public function getSchemas() {
+    return $this->siteSchemas;
+  }
+
+  public function getSchema($schemaPath) {
+    return $this->siteSchemas[$schemaPath];
+  }
+
+  /**
+   * Get the array of site schema types.
+   *
+   * @return array
+   *   An array of typed data ids (entity_type and bundle) keyed by
+   *   corresponding site schema URI.
+   */
+  public function getTypes() {
+    $cid = 'rdf:site_schema:types';
+    $cache = $this->cache->get($cid);
+    if (!$cache) {
+      $this->writeCache();
+      $cache = $this->cache->get($cid);
+    }
+    return $cache->data;
+  }
+
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/RdfMappingEventTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/RdfMappingEventTest.php
new file mode 100644
index 0000000..d71a652
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/RdfMappingEventTest.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * @file
+ * Contains RdfMappingEventTest.
+ */
+
+namespace Drupal\rdf\Tests;
+
+use Drupal\Core\Cache\DatabaseBackend;
+use Drupal\rdf\RdfMappingManager;
+use Drupal\rdf\EventSubscriber\MappingSubscriber;
+use Drupal\rdf_test_mapping\EventSubscriber\TestMappingSubscriber;
+use Drupal\rdf\SiteSchema\BundleSchema;
+use Drupal\rdf\SiteSchema\SiteSchema;
+use Drupal\rdf\SiteSchema\SiteSchemaManager;
+use Drupal\simpletest\WebTestBase;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+
+/**
+ * Test the RDF mapping events.
+ *
+ * This is implemented as a WebTest because it depends on entity info.
+ */
+class RdfMappingEventTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('rdf', 'rdf_test_mapping', 'entity_test');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'RDF mapping tests',
+      'description' => 'Tests the event-based RDF mapping system.',
+      'group' => 'RDF',
+    );
+  }
+
+  /**
+   * Test that other modules can define incoming type mappings.
+   */
+  public function testMapInputType() {
+    $dispatcher = new EventDispatcher();
+    $dispatcher->addSubscriber(new MappingSubscriber());
+    $dispatcher->addSubscriber(new TestMappingSubscriber());
+    $siteSchemaManager = new SiteSchemaManager(new DatabaseBackend('cache'));
+    $mappingManager = new RdfMappingManager($dispatcher, $siteSchemaManager);
+
+    // Test that a site schema URI is mapped to itself. This is the default
+    // behavior.
+    $schema = new SiteSchema(SiteSchema::CONTENT_DEPLOYMENT);
+    $bundleSchema = $schema->bundle('entity_test', 'entity_test');
+    $siteSchemaType = $bundleSchema->getUri();
+    $typedDataIds = $mappingManager->getTypedDataIdsFromTypeUris(array($siteSchemaType));
+    $this->assertTrue($typedDataIds['bundle'] == 'entity_test', 'An internal site schema type URI is properly handled.');
+
+    // Test that a module can map an external URI to a site schema URI.
+    $typedDataIds = $mappingManager->getTypedDataIdsFromTypeUris(array(TestMappingSubscriber::STAGING_SITE_TYPE_URI));
+    $this->assertTrue($typedDataIds['bundle'] == 'entity_test', 'Modules can map external type URIs to a site schema type.');
+  }
+
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/SiteSchemaTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/SiteSchemaTest.php
new file mode 100644
index 0000000..fd1bb19
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/SiteSchemaTest.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * @file
+ * Contains SiteSchemaTest.
+ */
+
+namespace Drupal\rdf\Tests;
+
+use Drupal\rdf\SiteSchema\BundleSchema;
+use Drupal\rdf\SiteSchema\SiteSchema;
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests for RDF namespaces XML serialization.
+ */
+class SiteSchemaTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('rdf', 'entity_test');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'RDF site schema test',
+      'description' => 'Confirm that site-generated schemas are created for entity, bundle, field, and field property.',
+      'group' => 'RDF',
+    );
+  }
+
+  /**
+   * Tests site-generated schema.
+   */
+  function testSiteSchema() {
+    $entityType = $bundle = 'entity_test';
+    $schema = new SiteSchema(SiteSchema::SYNDICATION);
+    $schemaPath = 'site-schema/syndication/';
+
+    // Bundle.
+    $bundleSchema = $schema->bundle($entityType, $bundle);
+    $bundleUri = url("$schemaPath$entityType/$bundle", array('absolute' => TRUE));
+    $bundleProperties = array(
+      'http://www.w3.org/2000/01/rdf-schema#isDefinedBy' => url($schemaPath, array('absolute' => TRUE)),
+      'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' => 'http://www.w3.org/2000/01/rdf-schema#class',
+      'http://www.w3.org/2000/01/rdf-schema#subClassOf' => url("$schemaPath$entityType", array('absolute' => TRUE)),
+    );
+
+    $this->assertEqual($bundleSchema->getUri(), $bundleUri, 'Bundle term URI is generated correctly.');
+    $this->assertEqual($bundleSchema->getProperties(), $bundleProperties, 'Bundle term properties are generated correctly.');
+  }
+
+}
diff --git a/core/modules/rdf/tests/rdf_test_mapping/lib/Drupal/rdf_test_mapping/EventSubscriber/TestMappingSubscriber.php b/core/modules/rdf/tests/rdf_test_mapping/lib/Drupal/rdf_test_mapping/EventSubscriber/TestMappingSubscriber.php
new file mode 100644
index 0000000..1646ef5
--- /dev/null
+++ b/core/modules/rdf/tests/rdf_test_mapping/lib/Drupal/rdf_test_mapping/EventSubscriber/TestMappingSubscriber.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * @file
+ * Contains TestMappingSubscriber.
+ */
+
+namespace Drupal\rdf_test_mapping\EventSubscriber;
+
+use Drupal\rdf\RdfMappingEvents;
+use Drupal\rdf\SiteSchema\BundleSchema;
+use Drupal\rdf\SiteSchema\SiteSchema;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+class TestMappingSubscriber implements EventSubscriberInterface {
+
+  const STAGING_SITE_TYPE_URI = 'http://staging.com/entity_test_bundle';
+
+  /**
+   * Demonstrate mapping between external type and site schema type.
+   *
+   * @param \Drupal\rdf\MapTypesFromInputEvent $event
+   */
+  public function mapTypesFromInput($event) {
+    $inputUris = $event->getInputUris();
+    $siteSchemaTypes = $event->getSiteSchemaTypes();
+
+    // This mapping between an external type and a site schema type would be
+    // managed by something in the implementing module, such as a database
+    // table. For the test, manually map a fake external URI to the site schema
+    // URI for the test entity.
+    $schema = new SiteSchema(SiteSchema::CONTENT_DEPLOYMENT);
+    $bundleSchema = $schema->bundle('entity_test', 'entity_test');
+    $siteSchemaType = $bundleSchema->getUri();
+    $mapping = array(
+      self::STAGING_SITE_TYPE_URI => $siteSchemaType,
+    );
+
+    foreach ($inputUris as $inputUri) {
+      // If the incoming URI is mapped in the mapping array, and the value of
+      // that mapping is found in the cache of site schema types, then set the
+      // site schema URI.
+      if (isset($mapping[$inputUri]) && isset($siteSchemaTypes[$mapping[$inputUri]])) {
+        $event->setSiteSchemaUri($mapping[$inputUri]);
+      }
+    }
+  }
+
+  /**
+   * Implements EventSubscriberInterface::getSubscribedEvents().
+   */
+  static function getSubscribedEvents() {
+    $events[RdfMappingEvents::MAP_TYPES_FROM_INPUT] = 'mapTypesFromInput';
+    return $events;
+  }
+
+}
diff --git a/core/modules/rdf/tests/rdf_test_mapping/lib/Drupal/rdf_test_mapping/RdfTestMappingBundle.php b/core/modules/rdf/tests/rdf_test_mapping/lib/Drupal/rdf_test_mapping/RdfTestMappingBundle.php
new file mode 100644
index 0000000..3de3980
--- /dev/null
+++ b/core/modules/rdf/tests/rdf_test_mapping/lib/Drupal/rdf_test_mapping/RdfTestMappingBundle.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file
+ * Contains RdfTestMappingBundle.
+ */
+
+namespace Drupal\rdf_test_mapping;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+/**
+ * RDF dependency injection container.
+ */
+class RdfTestMappingBundle extends Bundle {
+
+  /**
+   * Overrides Symfony\Component\HttpKernel\Bundle\Bundle::build().
+   */
+  public function build(ContainerBuilder $container) {
+    // Mapping subscriber.
+    $container->register('rdf_test_mapping.mapping', 'Drupal\rdf_test_mapping\EventSubscriber\TestMappingSubscriber')
+      ->addTag('event_subscriber');
+  }
+
+}
diff --git a/core/modules/rdf/tests/rdf_test_mapping/rdf_test_mapping.info b/core/modules/rdf/tests/rdf_test_mapping/rdf_test_mapping.info
new file mode 100644
index 0000000..d04be41
--- /dev/null
+++ b/core/modules/rdf/tests/rdf_test_mapping/rdf_test_mapping.info
@@ -0,0 +1,6 @@
+name = "RDF module mapping test"
+description = "Test mapping subscriber for RDF mapping tests."
+package = Testing
+core = 8.x
+hidden = TRUE
+dependencies[] = rdf
diff --git a/core/modules/rdf/tests/rdf_test_mapping/rdf_test_mapping.module b/core/modules/rdf/tests/rdf_test_mapping/rdf_test_mapping.module
new file mode 100644
index 0000000..b3d9bbc
--- /dev/null
+++ b/core/modules/rdf/tests/rdf_test_mapping/rdf_test_mapping.module
@@ -0,0 +1 @@
+<?php
