diff --git a/core/includes/common.inc b/core/includes/common.inc
index 9958553..86c9c6e 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -1904,8 +1904,6 @@ function format_date($timestamp, $type = 'medium', $format = '', $timezone = NUL
 /**
  * Returns an ISO8601 formatted date based on the given date.
  *
- * Callback for use within hook_rdf_mapping() implementations.
- *
  * @param $date
  *   A UNIX timestamp.
  *
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 6f2795a..45834df 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -2016,48 +2016,6 @@ function comment_ranking() {
 }
 
 /**
- * Implements hook_rdf_mapping().
- */
-function comment_rdf_mapping() {
-  return array(
-    array(
-      'type' => 'comment',
-      'bundle' => RDF_DEFAULT_BUNDLE,
-      'mapping' => array(
-        'rdftype' => array('sioc:Post', 'sioct:Comment'),
-        'title' => array(
-          'predicates' => array('dc:title'),
-        ),
-        'created' => array(
-          'predicates' => array('dc:date', 'dc:created'),
-          'datatype' => 'xsd:dateTime',
-          'callback' => 'date_iso8601',
-        ),
-        'changed' => array(
-          'predicates' => array('dc:modified'),
-          'datatype' => 'xsd:dateTime',
-          'callback' => 'date_iso8601',
-        ),
-        'comment_body' => array(
-          'predicates' => array('content:encoded'),
-        ),
-        'pid' => array(
-          'predicates' => array('sioc:reply_of'),
-          'type' => 'rel',
-        ),
-        'uid' => array(
-          'predicates' => array('sioc:has_creator'),
-          'type' => 'rel',
-        ),
-        'name' => array(
-          'predicates' => array('foaf:name'),
-        ),
-      ),
-    ),
-  );
-}
-
-/**
  * Implements hook_file_download_access().
  */
 function comment_file_download_access($field, EntityInterface $entity, File $file) {
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 872eb95..85b9c66 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -1306,7 +1306,7 @@ function forum_rdf_mapping() {
         'rdftype' => array('sioc:Post', 'sioct:BoardPost'),
         'taxonomy_forums' => array(
           'predicates' => array('sioc:has_container'),
-          'type' => 'rel',
+          'mapping_type' => 'rel',
         ),
       ),
     ),
diff --git a/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityNormalizer.php b/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityNormalizer.php
index ab662b3..6d88dbf 100644
--- a/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityNormalizer.php
+++ b/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityNormalizer.php
@@ -8,7 +8,7 @@
 namespace Drupal\jsonld;
 
 use Drupal\jsonld\JsonldNormalizerBase;
-use Drupal\rdf\RdfMappingException;
+use Drupal\rdf\Mapping\RdfMappingException;
 use Symfony\Component\Serializer\Exception\UnexpectedValueException;
 use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
 
diff --git a/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityWrapper.php b/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityWrapper.php
index a2e49e3..957bd4a 100644
--- a/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityWrapper.php
+++ b/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityWrapper.php
@@ -85,7 +85,7 @@ public function getId() {
   /**
    * Get the type URI.
    *
-   * @todo Once RdfMappingManager has a mapOutputTypes event, use that instead
+   * @todo Once RdfMappingManager has a mapBundleForOutput event, use that instead
    * of simply returning the site schema URI.
    */
   public function getTypeUri() {
@@ -93,12 +93,12 @@ public function getTypeUri() {
     $bundle = $this->entity->bundle();
     switch ($this->format) {
       case 'drupal_jsonld':
-        $schema_path = SiteSchema::CONTENT_DEPLOYMENT;
+        $schema_id = SiteSchema::CONTENT_DEPLOYMENT;
         break;
       case 'jsonld':
-        $schema_path = SiteSchema::SYNDICATION;
+        $schema_id = SiteSchema::SYNDICATION;
     }
-    $schema = $this->siteSchemaManager->getSchema($schema_path);
+    $schema = $this->siteSchemaManager->getSchema($schema_id);
     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 e898da8..ee6d09f 100644
--- a/core/modules/jsonld/lib/Drupal/jsonld/JsonldNormalizerBase.php
+++ b/core/modules/jsonld/lib/Drupal/jsonld/JsonldNormalizerBase.php
@@ -8,7 +8,7 @@
 namespace Drupal\jsonld;
 
 use ReflectionClass;
-use Drupal\rdf\RdfMappingManager;
+use Drupal\rdf\Mapping\RdfMappingManager;
 use Drupal\rdf\SiteSchema\SiteSchemaManager;
 use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
 use Symfony\Component\Serializer\Normalizer\SerializerAwareNormalizer;
@@ -42,7 +42,7 @@
   /**
    * The RDF mapping manager.
    *
-   * @var \Drupal\rdf\RdfMappingManager
+   * @var \Drupal\rdf\Mapping\RdfMappingManager
    */
   protected $rdfMappingManager;
 
@@ -51,7 +51,7 @@
    *
    * @param \Drupal\rdf\SiteSchema\SiteSchemaManager $site_schema_manager
    *   The site schema manager.
-   * @param \Drupal\rdf\RdfMappingManager $rdf_mapping_manager
+   * @param \Drupal\rdf\Mapping\RdfMappingManager $rdf_mapping_manager
    *   The RDF mapping manager.
    */
   public function __construct(SiteSchemaManager $site_schema_manager, RdfMappingManager $rdf_mapping_manager) {
diff --git a/core/modules/jsonld/lib/Drupal/jsonld/Tests/JsonldTestSetupHelper.php b/core/modules/jsonld/lib/Drupal/jsonld/Tests/JsonldTestSetupHelper.php
index d0eb0c3..f83a70d 100644
--- a/core/modules/jsonld/lib/Drupal/jsonld/Tests/JsonldTestSetupHelper.php
+++ b/core/modules/jsonld/lib/Drupal/jsonld/Tests/JsonldTestSetupHelper.php
@@ -12,7 +12,7 @@
 use Drupal\jsonld\JsonldEntityNormalizer;
 use Drupal\jsonld\JsonldEntityReferenceNormalizer;
 use Drupal\jsonld\JsonldFieldItemNormalizer;
-use Drupal\rdf\RdfMappingManager;
+use Drupal\rdf\Mapping\RdfMappingManager;
 use Drupal\rdf\EventSubscriber\MappingSubscriber;
 use Drupal\rdf\SiteSchema\SiteSchemaManager;
 use Symfony\Component\EventDispatcher\EventDispatcher;
@@ -33,7 +33,7 @@ class JsonldTestSetupHelper {
   /**
    * The RDF mapping manager.
    *
-   * @var \Drupal\rdf\RdfMappingManager
+   * @var \Drupal\rdf\Mapping\RdfMappingManager
    */
   protected $rdfMappingManager;
 
@@ -87,7 +87,7 @@ public function getSiteSchemaManager() {
   /**
    * Get the RdfMappingManager object.
    *
-   * @return \Drupal\rdf\RdfMappingManager
+   * @return \Drupal\rdf\Mapping\RdfMappingManager
    *   The RdfMappingManager, which is also injected into the Normalizers.
    */
   public function getRdfMappingManager() {
diff --git a/core/modules/jsonld/lib/Drupal/jsonld/Tests/RdfSchemaSerializationTest.php b/core/modules/jsonld/lib/Drupal/jsonld/Tests/RdfSchemaSerializationTest.php
index eacf9df..bc3f7ee 100644
--- a/core/modules/jsonld/lib/Drupal/jsonld/Tests/RdfSchemaSerializationTest.php
+++ b/core/modules/jsonld/lib/Drupal/jsonld/Tests/RdfSchemaSerializationTest.php
@@ -47,7 +47,7 @@ function testSchemaSerialization() {
     $parsed_term = $decoded[0];
 
     $this->assertEqual($parsed_term->{'@id'}, $bundle_schema->getUri(), 'JSON-LD for schema term uses correct @id.');
-    $this->assertEqual($parsed_term->{'@type'}, 'http://www.w3.org/2000/01/rdf-schema#class', 'JSON-LD for schema term uses correct @type.');
+    $this->assertEqual($parsed_term->{'@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.
     $array_keys = array_keys((array) $parsed_term);
     $this->assertEqual(array('@id', '@type'), array_slice($array_keys, 0, 2), 'JSON-LD keywords are placed before other properties.');
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 4be9596..896dc75 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -847,53 +847,6 @@ function node_type_set_defaults($info = array()) {
 }
 
 /**
- * Implements hook_rdf_mapping().
- */
-function node_rdf_mapping() {
-  return array(
-    array(
-      'type' => 'node',
-      'bundle' => RDF_DEFAULT_BUNDLE,
-      'mapping' => array(
-        'rdftype' => array('sioc:Item', 'foaf:Document'),
-        'title' => array(
-          'predicates' => array('dc:title'),
-        ),
-        'created' => array(
-          'predicates' => array('dc:date', 'dc:created'),
-          'datatype' => 'xsd:dateTime',
-          'callback' => 'date_iso8601',
-        ),
-        'changed' => array(
-          'predicates' => array('dc:modified'),
-          'datatype' => 'xsd:dateTime',
-          'callback' => 'date_iso8601',
-        ),
-        'body' => array(
-          'predicates' => array('content:encoded'),
-        ),
-        'uid' => array(
-          'predicates' => array('sioc:has_creator'),
-          'type' => 'rel',
-        ),
-        'name' => array(
-          'predicates' => array('foaf:name'),
-        ),
-        'comment_count' => array(
-          'predicates' => array('sioc:num_replies'),
-          'datatype' => 'xsd:integer',
-        ),
-        'last_activity' => array(
-          'predicates' => array('sioc:last_activity_date'),
-          'datatype' => 'xsd:dateTime',
-          'callback' => 'date_iso8601',
-        ),
-      ),
-    ),
-  );
-}
-
-/**
  * Determines whether a node hook exists.
  *
  * @param string $type
diff --git a/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/MappingSubscriber.php b/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/MappingSubscriber.php
index 764c2f9..f168fa0 100644
--- a/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/MappingSubscriber.php
+++ b/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/MappingSubscriber.php
@@ -6,7 +6,7 @@
 
 namespace Drupal\rdf\EventSubscriber;
 
-use Drupal\rdf\RdfMappingEvents;
+use Drupal\rdf\Mapping\RdfMappingEvents;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
@@ -21,10 +21,10 @@ class MappingSubscriber implements EventSubscriberInterface {
    * mapping is unnecessary. Mapping is only necessary if the incoming type URI
    * is from an external vocabulary.
    *
-   * @param \Drupal\rdf\MapTypesFromInputEvent $event
+   * @param \Drupal\rdf\Mapping\MapTypesFromInputEvent $event
    *   The mapping event.
    */
-  public function mapTypesFromInput(\Drupal\rdf\MapTypesFromInputEvent $event) {
+  public function mapTypesFromInput(\Drupal\rdf\Mapping\MapTypesFromInputEvent $event) {
     $input_uris = $event->getInputUris();
     $site_schema_types = $event->getSiteSchemaTypes();
     foreach ($input_uris as $input_uri) {
@@ -35,11 +35,54 @@ public function mapTypesFromInput(\Drupal\rdf\MapTypesFromInputEvent $event) {
     }
   }
 
+  public function mapBundleForOutput(\Drupal\rdf\Mapping\MapBundleForOutputEvent $event) {
+    $term_schema = $event->getTermSchema();
+    // @todo When the $schema is SYNDICATION, allow sites to exclude the site
+    // schema URI from the mapping.
+    $site_schema_curie = $term_schema->getCurie();
+    $event->addTypes(array($site_schema_curie));
+
+    $config = $term_schema->getMappingConfig();
+    $types = $config->get('types');
+    if (!empty($types)) {
+      $event->addTypes($types);
+    }
+  }
+
+  public function mapFieldForOutput(\Drupal\rdf\Mapping\MapFieldForOutputEvent $event) {
+    $term_schema = $event->getTermSchema();
+    // @todo When the $schema is SYNDICATION, allow sites to exclude the site
+    // schema URI from the mapping.
+    $site_schema_curie = $term_schema->getCurie();
+    $event->addPredicates(array($site_schema_curie));
+
+    $config = $term_schema->getMappingConfig();
+    $properties = $config->get('properties');
+    if (!empty($properties)) {
+      $event->addPredicates($properties);
+      $datatype = $config->get('datatype');
+      $datatype_callback = $config->get('datatype_callback');
+      $mapping_type = $config->get('mapping_type');
+
+      if (isset($datatype)) {
+        $event->setDatatype($datatype);
+      }
+      if (isset($datatype_callback)) {
+        $event->setDatatypeCallback($datatype_callback);
+      }
+      if (isset($mapping_type)) {
+        $event->setMappingType($mapping_type);
+      }
+    }
+  }
+
   /**
    * Implements EventSubscriberInterface::getSubscribedEvents().
    */
   static function getSubscribedEvents() {
     $events[RdfMappingEvents::MAP_TYPES_FROM_INPUT] = 'mapTypesFromInput';
+    $events[RdfMappingEvents::MAP_BUNDLE_FOR_OUTPUT] = 'mapBundleForOutput';
+    $events[RdfMappingEvents::MAP_FIELD_FOR_OUTPUT] = 'mapFieldForOutput';
     return $events;
   }
 }
diff --git a/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/RouteSubscriber.php b/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/RouteSubscriber.php
index 123493b..cf04c94 100644
--- a/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/RouteSubscriber.php
+++ b/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/RouteSubscriber.php
@@ -50,10 +50,10 @@ public function routes(RouteBuildEvent $event) {
     foreach ($this->siteSchemaManager->getSchemas() as $schema) {
       $routes = $schema->getRoutes();
       foreach ($routes as $controller => $pattern) {
-        $schema_path = $schema->getPath();
+        $schema_id = $schema->getId();
         $route = new Route($pattern, array(
           '_controller' => 'Drupal\rdf\SiteSchema\SchemaController::' . $controller,
-          'schema_path' => $schema_path,
+          'schema_id' => $schema_id,
         ), array(
           '_method' => 'GET',
           '_access' => 'TRUE',
@@ -61,7 +61,7 @@ public function routes(RouteBuildEvent $event) {
         // 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.
-        $route_name = 'rdf.' . str_replace(array('-','/'), array('_', '.'), substr_replace($schema_path ,"",-1));
+        $route_name = 'rdf.' . str_replace(array('-','/'), array('_', '.'), substr_replace($schema_id ,"",-1));
         $collection->add($route_name, $route);
       }
     }
diff --git a/core/modules/rdf/lib/Drupal/rdf/MapTypesFromInputEvent.php b/core/modules/rdf/lib/Drupal/rdf/MapTypesFromInputEvent.php
deleted file mode 100644
index c54fe0d..0000000
--- a/core/modules/rdf/lib/Drupal/rdf/MapTypesFromInputEvent.php
+++ /dev/null
@@ -1,91 +0,0 @@
-<?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 $input_uris
-   *   An array of incoming RDF type URIs.
-   * @param $site_schema_types
-   *   An array of entity_type/bundles, keyed by site schema type URI.
-   */
-  public function __construct($input_uris, $site_schema_types) {
-    $this->inputUris = $input_uris;
-    $this->siteSchemaTypes = $site_schema_types;
-    $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/Mapping/MapBundleForOutputEvent.php b/core/modules/rdf/lib/Drupal/rdf/Mapping/MapBundleForOutputEvent.php
new file mode 100644
index 0000000..9a359b0
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Mapping/MapBundleForOutputEvent.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\rdf\Mapping\MapBundleForOutputEvent.
+ */
+
+namespace Drupal\rdf\Mapping;
+
+use Symfony\Component\EventDispatcher\Event;
+
+class MapBundleForOutputEvent extends Event {
+
+  /**
+   * The site schema term to map.
+   *
+   * @var string
+   */
+  protected $termSchema;
+
+  /**
+   * An array of CURIEs to use for the RDF types.
+   *
+   * @var array
+   */
+  protected $types;
+
+  /**
+   * Constructor.
+   *
+   * @param string $term_schema
+   */
+  function __construct($term_schema) {
+    $this->termSchema = $term_schema;
+    $this->types = array();
+  }
+
+  public function getTermSchema() {
+    return $this->termSchema;
+  }
+
+  /**
+   * Add RDF types to the CURIEs array.
+   *
+   * @param array $curies
+   *
+   * @todo Add namespace IDs.
+   */
+  public function addTypes($curies) {
+    if (!is_array($curies)) {
+      $curies = array($curies);
+    }
+    $this->types = array_merge($this->types, $curies);
+  }
+
+  /**
+   * @return array
+   *   An array of CURIE strings/arrays.
+   */
+  public function getTypes() {
+    return $this->types;
+  }
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/Mapping/MapFieldForOutputEvent.php b/core/modules/rdf/lib/Drupal/rdf/Mapping/MapFieldForOutputEvent.php
new file mode 100644
index 0000000..ce4869a
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Mapping/MapFieldForOutputEvent.php
@@ -0,0 +1,105 @@
+<?php
+
+/**
+ * @file
+ * Contains MapFieldForOutputEvent.
+ */
+
+namespace Drupal\rdf\Mapping;
+
+use Symfony\Component\EventDispatcher\Event;
+
+class MapFieldForOutputEvent extends Event {
+
+  /**
+   * The site schema term to map.
+   *
+   * @var string
+   */
+  protected $termSchema;
+
+  /**
+   * An array of CURIEs to use for the RDF types.
+   *
+   * @var array
+   */
+  protected $predicates;
+
+  protected $datatype;
+
+  protected $datatypeCallback;
+
+  protected $mappingType;
+
+  /**
+   * Constructor.
+   *
+   * @param string $term_schema
+   */
+  function __construct($term_schema) {
+    $this->termSchema = $term_schema;
+    $this->predicates = array();
+  }
+
+  public function getTermSchema() {
+    return $this->termSchema;
+  }
+
+  /**
+   * Add CURIEs to the predicates array.
+   *
+   * @param array $curies
+   */
+  public function addPredicates($curies) {
+    if (!is_array($curies)) {
+      $curies = array($curies);
+    }
+    $this->predicates = array_merge($this->predicates, $curies);
+  }
+
+  /**
+   * Set the datatype.
+   *
+   * @param string|array $curie
+   */
+  public function setDatatype($curie) {
+    if (!is_array($curie)) {
+      $curie = array($curie);
+    }
+    $this->datatype = $curie;
+  }
+
+  /**
+   * Set the datatype callback.
+   *
+   * @param callable $callable
+   */
+  public function setDatatypeCallback($callable) {
+    $this->datatypeCallback = $callable;
+  }
+
+  /**
+   * Set the mapping type.
+   *
+   * @param string $mapping_type
+   */
+  public function setMappingType($mapping_type) {
+    $this->mappingType = $mapping_type;
+  }
+
+  public function getPredicates() {
+    return $this->predicates;
+  }
+
+  public function getDatatype() {
+    return $this->datatype;
+  }
+
+  public function getDatatypeCallback() {
+    return $this->datatypeCallback;
+  }
+
+  public function getMappingType() {
+    return $this->mappingType;
+  }
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/Mapping/MapTypesFromInputEvent.php b/core/modules/rdf/lib/Drupal/rdf/Mapping/MapTypesFromInputEvent.php
new file mode 100644
index 0000000..7ca6ea4
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Mapping/MapTypesFromInputEvent.php
@@ -0,0 +1,91 @@
+<?php
+
+/**
+ * @file
+ * Contains MapInputTypesEvent.
+ */
+
+namespace Drupal\rdf\Mapping;
+
+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 $input_uris
+   *   An array of incoming RDF type URIs.
+   * @param $site_schema_types
+   *   An array of entity_type/bundles, keyed by site schema type URI.
+   */
+  public function __construct($input_uris, $site_schema_types) {
+    $this->inputUris = $input_uris;
+    $this->siteSchemaTypes = $site_schema_types;
+    $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/Mapping/RdfMappingEvents.php b/core/modules/rdf/lib/Drupal/rdf/Mapping/RdfMappingEvents.php
new file mode 100644
index 0000000..5fea94a
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Mapping/RdfMappingEvents.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * @file
+ * Contains RdfMappingEvents.
+ */
+
+namespace Drupal\rdf\Mapping;
+
+/**
+ * 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\Mapping\RdfMappingManager
+   *
+   * @var string
+   */
+  const MAP_TYPES_FROM_INPUT = 'rdf.map_types_from_input';
+
+  /**
+   * Maps a bundle to corresponding RDF types.
+   *
+   * In contrast to MAP_TYPES_FROM_INPUT, this event is triggered when RDF is
+   * being published. Modules can use this event to add RDF types to a mapping,
+   * which will then be published in the site's RDFa, JSON-LD, etc. However, a
+   * module does not have to use this event to add RDF types. RDF module manages
+   * an RDF mapping configuration file for each bundle which modules can also
+   * use to register mappings.
+   *
+   * @todo Add a note about this being cached once it is.
+   *
+   * @see \Drupal\rdf\Mapping\RdfMappingManager
+   *
+   * @var string
+   */
+  const MAP_BUNDLE_FOR_OUTPUT = 'rdf.map_bundle_for_output';
+
+  /**
+   * Maps a bundle to corresponding RDF properties.
+   *
+   * In contrast to MAP_PROPERTIES_FROM_INPUT, this event is triggered when RDF
+   * is being published. Modules can use this event to add RDF properties to a
+   * mapping, which will then be published in the site's RDFa, JSON-LD, etc.
+   * However, a module does not have to use this event to add RDF properties.
+   * RDF module manages an RDF mapping configuration file for each field
+   * instance which modules can also use to register mappings.
+   *
+   * @todo Add a note about this being cached once it is.
+   *
+   * @see \Drupal\rdf\Mapping\RdfMappingManager
+   *
+   * @var string
+   */
+  const MAP_FIELD_FOR_OUTPUT = 'rdf.map_field_for_output';
+
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/Mapping/RdfMappingException.php b/core/modules/rdf/lib/Drupal/rdf/Mapping/RdfMappingException.php
new file mode 100644
index 0000000..8b4e116
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Mapping/RdfMappingException.php
@@ -0,0 +1,13 @@
+<?php
+
+/**
+ * @file
+ * Contains RdfMappingException.
+ */
+
+namespace Drupal\rdf\Mapping;
+
+/**
+ * Exception to use when no RDF mapping is found.
+ */
+class RdfMappingException extends \Exception { }
diff --git a/core/modules/rdf/lib/Drupal/rdf/Mapping/RdfMappingManager.php b/core/modules/rdf/lib/Drupal/rdf/Mapping/RdfMappingManager.php
new file mode 100644
index 0000000..70a0cf6
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Mapping/RdfMappingManager.php
@@ -0,0 +1,243 @@
+<?php
+
+/**
+ * @file
+ * Contains RdfMappingManager.
+ */
+
+namespace Drupal\rdf\Mapping;
+
+use ReflectionClass;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\rdf\Mapping\MapTypesFromInputEvent;
+use Drupal\rdf\Mapping\MapBundleForOutputEvent;
+use Drupal\rdf\Mapping\MapFieldForOutputEvent;
+use Drupal\rdf\Mapping\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 $site_schema_manager
+   *   The site schema manager.
+   */
+  public function __construct(EventDispatcherInterface $dispatcher, SiteSchemaManager $site_schema_manager) {
+    $this->dispatcher = $dispatcher;
+    $this->siteSchemaManager = $site_schema_manager;
+  }
+
+  /**
+   * Convert an array of RDF type URIs to the corresponding TypedData IDs.
+   *
+   * @param array $input_rdf_types
+   *   An array of URIs for the type.
+   *
+   * @return array
+   *   An array containing entity_type and bundle.
+   *
+   * @throws \Drupal\rdf\Mapping\RdfMappingException
+   */
+  public function getTypedDataIdsFromTypeUris($input_rdf_types) {
+    // Get the cache of site schema types.
+    $site_schema_types = $this->siteSchemaManager->getTypes();
+    // Map the RDF type from the incoming data to an RDF type defined in the
+    // internal site schema.
+    $type_uri = $this->mapTypesFromInput($input_rdf_types);
+    // If no site schema URI has been determined, then it's impossible to know
+    // what entity type to create. Throw an exception.
+    if ($type_uri == FALSE) {
+      throw new RdfMappingException(sprintf('No mapping to a site schema type URI found for incoming types (%s).', implode(',', $input_rdf_types)));
+    }
+    // Use the mapped RDF type URI to get the TypedData API ids the rest of the
+    // system uses (entity type and bundle).
+    return $site_schema_types[$type_uri];
+  }
+
+  /**
+   * Convert a bundle's Typed Data IDs to an RDF mapping array.
+   *
+   * Bundles will have different mappings based on whether the data is to be
+   * used for content deployment or for syndication. The site schema id is used
+   * to specify which to return.
+   *
+   * @param string $entity_type
+   *   The entity type of the bundle which the mapping corresponds to.
+   * @param string $bundle
+   *   The name of the bundle which the mapping corresponds to.
+   * @param string $schema_id
+   *   The id of the site schema which is being mapped to.
+   *
+   * @return array
+   *   The RDF mapping.
+   */
+  public function getBundleMapping($entity_type, $bundle, $schema_id = SiteSchema::SYNDICATION) {
+    $site_schema = $this->siteSchemaManager->getSchema($schema_id);
+    $term_schema = $site_schema->bundle($entity_type, $bundle);
+    // Get the mapping, which contains the RDF types used when publishing data.
+    return $this->mapBundleForOutput($term_schema);
+  }
+
+  /**
+   * Convert a field instance's Typed Data IDs to an RDF mapping array.
+   *
+   * Field instances will have different mappings based on whether the data is
+   * to be used for content deployment or for syndication. The site schema id
+   * is used to specify which to return.
+   *
+   * @param string $entity_type
+   *   The entity type of the field instance which the mapping corresponds to.
+   * @param string $bundle
+   *   The bundle of the field instance which the mapping corresponds to.
+   * @param string field_name
+   *   The field name of the field instance which the mapping corresponds to.
+   * @param string $schema_id
+   *   The id of the site schema which is being mapped to.
+   *
+   * @return array
+   *   The RDF mapping.
+   */
+  public function getFieldMapping($entity_type, $bundle, $field_name, $schema_id = SiteSchema::SYNDICATION) {
+    $site_schema = $this->siteSchemaManager->getSchema($schema_id);
+    $term_schema = $site_schema->field($entity_type, $bundle, $field_name);
+    return $this->mapFieldForOutput($term_schema);
+  }
+
+  /**
+   * Get the mapping config entity for a bundle.
+   *
+   * Bundles will have different mappings based on whether the data is to be
+   * used for content deployment or for syndication. The site schema id is used
+   * to specify which to return.
+   *
+   * @param string $entity_type
+   *   The entity type of the bundle which the mapping corresponds to.
+   * @param string $bundle
+   *   The name of the bundle which the mapping corresponds to.
+   * @param string $schema_id
+   *   The id of the site schema which is being mapped to.
+   *
+   * @return array
+   *   The RDF mapping config entity.
+   */
+  public function getBundleMappingConfig($entity_type, $bundle, $schema_id = SiteSchema::SYNDICATION) {
+    $bundle_schema = $this->siteSchemaManager->getSchema($schema_id)->bundle($entity_type, $bundle);
+    return $bundle_schema->getMappingConfig();
+  }
+
+  /**
+   * Get the field mapping config entity for a field instance.
+   *
+   * Field instances will have different mappings based on whether the data is
+   * to be used for content deployment or for syndication. The site schema id
+   * is used to specify which to return.
+   *
+   * @param string $entity_type
+   *   The entity type of the field instance which the mapping corresponds to.
+   * @param string $bundle
+   *   The bundle of the field instance which the mapping corresponds to.
+   * @param string field_name
+   *   The field name of the field instance which the mapping corresponds to.
+   * @param string $schema_id
+   *   The id of the site schema which is being mapped to.
+   *
+   * @return array
+   *   The RDF mapping.
+   */
+  public function getFieldMappingConfig($entity_type, $bundle, $field_name, $schema_id = SiteSchema::SYNDICATION) {
+    $field_schema = $this->siteSchemaManager->getSchema($schema_id)->field($entity_type, $bundle, $field_name);
+    return $field_schema->getMappingConfig();
+  }
+
+  /**
+   * Map an array of incoming URIs to an internal site schema URI.
+   *
+   * @param array $input_rdf_types
+   *   An array of RDF type URIs.
+   *
+   * @return string
+   *   The corresponding site schema type URI.
+   */
+  protected function mapTypesFromInput($input_rdf_types) {
+    // Create the event using the array of incoming RDF type URIs and the cache
+    // of internal site schema URIs.
+    $site_schema_types = $this->siteSchemaManager->getTypes();
+    $mapping_event = new MapTypesFromInputEvent($input_rdf_types, $site_schema_types);
+
+    // 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();
+  }
+
+  /**
+   * Map a bundle's site schema term to external vocabularies.
+   *
+   * @param \Drupal\rdf\SiteSchema\SchemaTermInterface $term_schema
+   *   The term object.
+   *
+   * @return string
+   *   The corresponding RDF mapping.
+   */
+  protected function mapBundleForOutput($term_schema) {
+    $mapping = array();
+    $map_event = new MapBundleForOutputEvent($term_schema);
+    $this->dispatcher->dispatch(RdfMappingEvents::MAP_BUNDLE_FOR_OUTPUT, $map_event);
+    $types = $map_event->getTypes();
+    if (!empty($types)) {
+      $mapping['types'] = $types;
+    }
+    return $mapping;
+  }
+
+  /**
+   * Map a field instance's site schema term to external vocabularies.
+   *
+   * @param \Drupal\rdf\SiteSchema\SchemaTermInterface $term_schema
+   *   The term object.
+   *
+   * @return string
+   *   The corresponding RDF mapping.
+   */
+  protected function mapFieldForOutput($term_schema) {
+    $mapping = array();
+    $map_event = new MapFieldForOutputEvent($term_schema);
+    $this->dispatcher->dispatch(RdfMappingEvents::MAP_FIELD_FOR_OUTPUT, $map_event);
+
+    $predicates = $map_event->getPredicates();
+    if (!empty($predicates)) {
+      $mapping['properties'] = $predicates;
+      $mapping['datatype'] = $map_event->getDatatype();
+      $mapping['datatype_callback'] = $map_event->getDatatypeCallback();
+      $mapping['mapping_type'] = $map_event->getMappingType();
+    }
+    return array_filter($mapping);
+  }
+
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/BundleRdfMapping.php b/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/BundleRdfMapping.php
new file mode 100644
index 0000000..01c0cd2
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/BundleRdfMapping.php
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * @file
+ * Contains BundleRdfMapping.
+ */
+
+namespace Drupal\rdf\Plugin\Core\Entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Defines the bundle RDF mapping entity.
+ *
+ * @Plugin(
+ *   id = "rdf_mapping_bundle",
+ *   label = @Translation("Bundle RDF mapping"),
+ *   module = "rdf",
+ *   controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ *   config_prefix = "rdf.mapping.bundle",
+ *   entity_keys = {
+ *     "id" = "mid",
+ *   }
+ * )
+ */
+class BundleRdfMapping extends ConfigEntityBase {
+
+  /**
+   * The RDF mapping ID.
+   *
+   * @var string
+   */
+  public $mid;
+
+  /**
+   * The schema of the mapped site schema term.
+   *
+   * @var string
+   */
+  public $schema_id;
+
+  /**
+   * The entity type of the corresponding bundle.
+   *
+   * @var string
+   */
+  public $entity_type;
+
+  /**
+   * The site schema term's corresponding bundle.
+   *
+   * @var string
+   */
+  public $bundle;
+
+  /**
+   * The types that are being mapped to this site schema term.
+   *
+   * @var array
+   */
+  public $types;
+
+  /**
+   * Implements Drupal\Core\Entity\EntityInterface::id().
+   */
+  public function id() {
+    return $this->mid;
+  }
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/FieldRdfMapping.php b/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/FieldRdfMapping.php
new file mode 100644
index 0000000..a38eee3
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/FieldRdfMapping.php
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * @file
+ * Contains FieldRdfMapping.
+ */
+
+namespace Drupal\rdf\Plugin\Core\Entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Defines the field RDF mapping entity.
+ *
+ * @Plugin(
+ *   id = "rdf_mapping_field",
+ *   label = @Translation("Field RDF mapping"),
+ *   module = "rdf",
+ *   controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ *   config_prefix = "rdf.mapping.field",
+ *   entity_keys = {
+ *     "id" = "mid",
+ *   }
+ * )
+ */
+class FieldRdfMapping extends ConfigEntityBase {
+
+  /**
+   * The RDF mapping ID.
+   *
+   * @var string
+   */
+  public $mid;
+
+  /**
+   * The schema of the mapped site schema term.
+   *
+   * @var string
+   */
+  public $schema_id;
+
+  /**
+   * The entity type of the bundle that the corresponding field is attached to.
+   *
+   * @var string
+   */
+  public $entity_type;
+
+  /**
+   * The bundle that the corresponding field is attached to.
+   *
+   * @var string
+   */
+  public $bundle;
+
+  /**
+   * The site schema term's corresponding field.
+   *
+   * @var string
+   */
+  public $field_name;
+
+  /**
+   * The RDF properties the field is mapped to.
+   *
+   * @var array
+   */
+  public $properties;
+
+  /**
+   * If this is a primitive, the datatype URI.
+   *
+   * @var string
+   */
+  public $datatype;
+
+  /**
+   * If this is a primitive, the callback to get a properly formatted value.
+   *
+   * @var callable
+   */
+  public $datatype_callback;
+
+  /**
+   * The type of mapping indicating if the target is a string or a resource.
+   *
+   * @todo deprecate in favor of EntityNG.
+   *
+   * @var string
+   */
+  public $mapping_type;
+
+  /**
+   * Implements Drupal\Core\Entity\EntityInterface::id().
+   */
+  public function id() {
+    return $this->mid;
+  }
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/RdfBundle.php b/core/modules/rdf/lib/Drupal/rdf/RdfBundle.php
index 40affcc..295ebfc 100644
--- a/core/modules/rdf/lib/Drupal/rdf/RdfBundle.php
+++ b/core/modules/rdf/lib/Drupal/rdf/RdfBundle.php
@@ -30,7 +30,7 @@ public function build(ContainerBuilder $container) {
     $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')
+    $container->register('rdf.mapping_manager', 'Drupal\rdf\Mapping\RdfMappingManager')
       ->addArgument(new Reference('event_dispatcher'))
       ->addArgument(new Reference('rdf.site_schema_manager'));
 
diff --git a/core/modules/rdf/lib/Drupal/rdf/RdfConstants.php b/core/modules/rdf/lib/Drupal/rdf/RdfConstants.php
index b64ae91..4ac035f 100644
--- a/core/modules/rdf/lib/Drupal/rdf/RdfConstants.php
+++ b/core/modules/rdf/lib/Drupal/rdf/RdfConstants.php
@@ -12,8 +12,9 @@
  */
 abstract class RdfConstants {
   const RDF_TYPE            = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type';
+  const RDF_PROPERTY        = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Property';
   // RDF Schema terms.
-  const RDFS_CLASS          = 'http://www.w3.org/2000/01/rdf-schema#class';
+  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';
diff --git a/core/modules/rdf/lib/Drupal/rdf/RdfMappingEvents.php b/core/modules/rdf/lib/Drupal/rdf/RdfMappingEvents.php
deleted file mode 100644
index 0e3fdae..0000000
--- a/core/modules/rdf/lib/Drupal/rdf/RdfMappingEvents.php
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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
deleted file mode 100644
index d96722f..0000000
--- a/core/modules/rdf/lib/Drupal/rdf/RdfMappingException.php
+++ /dev/null
@@ -1,13 +0,0 @@
-<?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
deleted file mode 100644
index 90588e9..0000000
--- a/core/modules/rdf/lib/Drupal/rdf/RdfMappingManager.php
+++ /dev/null
@@ -1,101 +0,0 @@
-<?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 $site_schema_manager
-   *   The site schema manager.
-   */
-  public function __construct(EventDispatcherInterface $dispatcher, SiteSchemaManager $site_schema_manager) {
-    $this->dispatcher = $dispatcher;
-    $this->siteSchemaManager = $site_schema_manager;
-  }
-
-  /**
-   * Convert an array of RDF type URIs to the corresponding TypedData IDs.
-   *
-   * @param array $input_rdf_types
-   *   An array of URIs for the type.
-   *
-   * @return array
-   *   An array containing entity_type and bundle.
-   *
-   * @throws \Drupal\rdf\RdfMappingException
-   */
-  public function getTypedDataIdsFromTypeUris($input_rdf_types) {
-    // Get the cache of site schema types.
-    $site_schema_types = $this->siteSchemaManager->getTypes();
-    // Map the RDF type from the incoming data to an RDF type defined in the
-    // internal site schema.
-    $type_uri = $this->mapTypesFromInput($input_rdf_types);
-    // If no site schema URI has been determined, then it's impossible to know
-    // what entity type to create. Throw an exception.
-    if ($type_uri == FALSE) {
-      throw new RdfMappingException(sprintf('No mapping to a site schema type URI found for incoming types (%s).', implode(',', $input_rdf_types)));
-    }
-    // Use the mapped RDF type URI to get the TypedData API ids the rest of the
-    // system uses (entity type and bundle).
-    return $site_schema_types[$type_uri];
-  }
-
-  /**
-   * Map an array of incoming URIs to an internal site schema URI.
-   *
-   * @param array $input_rdf_types
-   *   An array of RDF type URIs.
-   *
-   * @return string
-   *   The corresponding site schema type URI.
-   */
-  protected function mapTypesFromInput($input_rdf_types) {
-    // Create the event using the array of incoming RDF type URIs and the cache
-    // of internal site schema URIs.
-    $site_schema_types = $this->siteSchemaManager->getTypes();
-    $mapping_event = new MapTypesFromInputEvent($input_rdf_types, $site_schema_types);
-
-    // 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
index 2c92696..ff79559 100644
--- a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/BundleSchema.php
+++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/BundleSchema.php
@@ -22,6 +22,8 @@ class BundleSchema extends EntitySchema {
    */
   public static $uriPattern = '{entity_type}/{bundle}';
 
+  protected $configPrefix = 'rdf.mapping.bundle';
+
   /**
    * The bundle that this term identifies.
    *
@@ -45,11 +47,14 @@ public function __construct($site_schema, $entity_type, $bundle) {
   }
 
   /**
-   * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getUri().
+   * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getPath().
    */
-  public function getUri() {
-    $path = str_replace(array('{entity_type}', '{bundle}'), array($this->entityType, $this->bundle), static::$uriPattern);
-    return $this->siteSchema->getUri() . $path;
+  public function getPath() {
+    return $this->siteSchema->getPath() . $this->prepareUriPattern();
+  }
+
+  protected function prepareUriPattern() {
+    return str_replace(array('{entity_type}', '{bundle}'), array($this->entityType, $this->bundle), static::$uriPattern);
   }
 
   /**
@@ -61,4 +66,10 @@ public function getProperties() {
     return $properties;
   }
 
+  public function getTermIds() {
+    $keys = parent::getTermIds();
+    $keys['bundle'] = $this->bundle;
+    return $keys;
+  }
+
 }
diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/EntitySchema.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/EntitySchema.php
index 48a69fc..5d4dd7b 100644
--- a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/EntitySchema.php
+++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/EntitySchema.php
@@ -54,11 +54,11 @@ public function getGraph() {
   }
 
   /**
-   * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getUri().
+   * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getPath().
    */
-  public function getUri() {
+  public function getPath() {
     $path = str_replace('{entity_type}', $this->entityType , static::$uriPattern);
-    return $this->siteSchema->getUri() . $path;
+    return $this->siteSchema->getPath() . $path;
   }
 
   /**
@@ -70,4 +70,11 @@ public function getProperties() {
     return $properties;
   }
 
+  public function getTermIds() {
+    return array(
+      'schema_id' => $this->siteSchema->getId(),
+      'entity_type' => $this->entityType,
+    );
+  }
+
 }
diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/FieldSchema.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/FieldSchema.php
new file mode 100644
index 0000000..e7c9425
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/FieldSchema.php
@@ -0,0 +1,105 @@
+<?php
+
+/**
+ * @file
+ * Contains FieldSchema.
+ */
+
+namespace Drupal\rdf\SiteSchema;
+
+use Drupal\rdf\RdfConstants;
+use Drupal\rdf\SiteSchema\SchemaTermBase;
+
+/**
+ * Defines RDF terms corresponding to Drupal field instances.
+ */
+class FieldSchema extends SchemaTermBase {
+
+  /**
+   * The URI pattern for entity site schema terms.
+   *
+   * @var string
+   */
+  public static $uriPattern = '{entity_type}/{bundle}/{field_name}';
+
+  protected $configPrefix = 'rdf.mapping.field';
+
+  /**
+   * The entity type that this term identifies.
+   *
+   * @var string
+   */
+  protected $entityType;
+
+  /**
+   * The entity type that this term identifies.
+   *
+   * @var string
+   */
+  protected $bundle;
+
+  /**
+   * The entity type that this term identifies.
+   *
+   * @var string
+   */
+  protected $fieldName;
+
+  /**
+   * Constructor.
+   *
+   * @param \Drupal\rdf\SiteSchema\SiteSchema $siteSchema
+   *   The schema the term is defined in.
+   * @param string $entity_type
+   *   The entity type of the bundle the field is attached to.
+   * @param $bundle
+   *   The bundle the field is attached to.
+   * @param $field_name
+   *   The field name.
+   */
+  public function __construct($siteSchema, $entity_type, $bundle, $field_name) {
+    parent::__construct($siteSchema);
+    $this->entityType = $entity_type;
+    $this->bundle = $bundle;
+    $this->fieldName = $field_name;
+  }
+
+  /**
+   * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getGraph().
+   *
+   * @todo Loop through all fields and add their RDF descriptions.
+   */
+  public function getGraph() {
+    // @todo Implement this.
+  }
+
+  /**
+   * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getPath().
+   */
+  public function getPath() {
+    return $this->siteSchema->getPath() . $this->prepareUriPattern();
+  }
+
+  protected function prepareUriPattern() {
+    return str_replace(array('{entity_type}', '{bundle}', '{field_name}'), array($this->entityType, $this->bundle, $this->fieldName), static::$uriPattern);
+  }
+
+  /**
+   * Overrides \Drupal\rdf\SiteSchema\SchemaTermBase::getProperties().
+   */
+  public function getProperties() {
+    $properties = parent::getProperties();
+    $properties[RdfConstants::RDF_TYPE] = RdfConstants::RDF_PROPERTY;
+    return $properties;
+  }
+
+  public function getTermIds() {
+    return array(
+      'schema_id' => $this->siteSchema->getId(),
+      'entity_type' => $this->entityType,
+      'bundle' => $this->bundle,
+      'field_name' => $this->fieldName,
+    );
+  }
+
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaController.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaController.php
index acd3f8e..9e36907 100644
--- a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaController.php
+++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaController.php
@@ -42,7 +42,7 @@ public function setContainer(ContainerInterface $container = NULL) {
    *   The entity type.
    * @param string $bundle
    *   The entity bundle.
-   * @param string $schema_path
+   * @param string $schema_id
    *   The relative base path for the schema.
    *
    * @return \Symfony\Component\HttpFoundation\Response
@@ -50,7 +50,7 @@ public function setContainer(ContainerInterface $container = NULL) {
    *
    * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
    */
-  public function bundle($entity_type, $bundle, $schema_path) {
+  public function bundle($entity_type, $bundle, $schema_id) {
     if (!$entity_info = entity_get_info($entity_type)) {
       throw new NotFoundHttpException(t('Entity type @entity_type not found', array('@entity_type' => $entity_type)));
     }
@@ -60,7 +60,7 @@ public function bundle($entity_type, $bundle, $schema_path) {
 
     $serializer = $this->container->get('serializer');
     $site_schema_manager = $this->container->get('rdf.site_schema_manager');
-    $schema = $site_schema_manager->getSchema($schema_path);
+    $schema = $site_schema_manager->getSchema($schema_id);
     // @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
index d7afd1c..86ecf19 100644
--- a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermBase.php
+++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermBase.php
@@ -28,6 +28,8 @@
    */
   protected $siteSchema;
 
+  protected $configPrefix;
+
   /**
    * Constructor.
    *
@@ -39,6 +41,17 @@ public function __construct($site_schema) {
   }
 
   /**
+   * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getUri().
+   */
+  function getUri() {
+    return url($this->getPath(), array('absolute' => TRUE));
+  }
+
+  public function getCurie() {
+    return $this->siteSchema->getPrefix() . ':' . $this->prepareUriPattern();
+  }
+
+  /**
    * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getProperties().
    */
   public function getProperties() {
@@ -47,4 +60,31 @@ public function getProperties() {
     );
   }
 
+  /**
+   * Get the mapping config object.
+   *
+   * Each site schema term can be mapped to external RDF terms. This config
+   * entity stores such mappings.
+   *
+   * @return \Drupal\Core\Config\Config
+   *   The mapping config object corresponding to this site schema term
+   */
+  public function getMappingConfig() {
+    $config_id = implode('.', $this->getTermIds());
+    $config = config($this->configPrefix . '.' . $config_id);
+
+    // If the entity type isn't set, then the config didn't exist before and the
+    // config factory created a new config entity. Fill in the keys and save it.
+    $entity_type = $config->get('entity_type');
+    if (empty($entity_type)) {
+      $config->set('mid', $config_id);
+      foreach ($this->getTermIds() as $key => $value) {
+        $config->set($key, $value);
+      }
+      $config->save();
+    }
+
+    return $config;
+  }
+
 }
diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermInterface.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermInterface.php
index 342b51e..239126e 100644
--- a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermInterface.php
+++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermInterface.php
@@ -41,4 +41,15 @@ public function getProperties();
    *   The URI of the term.
    */
   public function getUri();
+
+  /**
+   * Get the relative path 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 relative path of the term.
+   */
+  public function getPath();
 }
diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchema.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchema.php
index e3153d1..dbb36d1 100644
--- a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchema.php
+++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchema.php
@@ -19,8 +19,8 @@ class SiteSchema {
   // 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/';
+  const CONTENT_DEPLOYMENT = 'content-deployment';
+  const SYNDICATION     = 'syndication';
 
   /**
    * The relative base path of the instantiated schema.
@@ -29,20 +29,26 @@ class SiteSchema {
    */
   protected $schemaPath;
 
+  protected $schemaId;
+
+  protected $prefix;
+
   /**
    * Constructor.
    *
-   * @param string $schema_path
+   * @param string $schema_id
    *   The schema path constant, used to determine which schema to instantiate.
    *
    * @throws \UnexpectedValueException
    */
-  public function __construct($schema_path) {
+  public function __construct($schema_id) {
     $valid_paths = array(self::CONTENT_DEPLOYMENT, self::SYNDICATION);
-    if (!in_array($schema_path, $valid_paths)) {
-      throw new \UnexpectedValueException(sprintf('%s is not a valid site schema path. Schema path must be one of %s.'), $schema_path, implode(', ', $valid_paths));
+    if (!in_array($schema_id, $valid_paths)) {
+      throw new \UnexpectedValueException(sprintf('%s is not a valid site schema path. Schema path must be one of %s.'), $schema_id, implode(', ', $valid_paths));
     }
-    $this->schemaPath = $schema_path;
+    $this->schemaId = $schema_id;
+    $this->schemaPath = 'site-schema/' . $schema_id . '/';
+    $this->prefix = ($schema_id == self::CONTENT_DEPLOYMENT) ? 'site-cd' : 'site-syn';
   }
 
   /**
@@ -60,6 +66,13 @@ public function bundle($entity_type, $bundle) {
   }
 
   /**
+   * Get a field instance's term definition in this vocabulary.
+   */
+  public function field($entity_type, $bundle, $field_name) {
+    return new FieldSchema($this, $entity_type, $bundle, $field_name);
+  }
+
+  /**
    * Get the URI of the schema.
    *
    * @return string
@@ -69,6 +82,10 @@ public function getUri() {
     return url($this->schemaPath, array('absolute' => TRUE));
   }
 
+  public function getId() {
+    return $this->schemaId;
+  }
+
   /**
    * Get the relative base path of the schema.
    */
@@ -76,6 +93,10 @@ public function getPath() {
     return $this->schemaPath;
   }
 
+  public function getPrefix() {
+    return $this->prefix;
+  }
+
   /**
    * Get the routes for the types of terms defined in this schema.
    *
diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchemaManager.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchemaManager.php
index e42b868..a89c417 100644
--- a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchemaManager.php
+++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchemaManager.php
@@ -67,8 +67,8 @@ public function getSchemas() {
     return $this->siteSchemas;
   }
 
-  public function getSchema($schema_path) {
-    return $this->siteSchemas[$schema_path];
+  public function getSchema($schema_id) {
+    return $this->siteSchemas[$schema_id];
   }
 
   /**
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/CommentAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/CommentAttributesTest.php
index bce0f35..e78e1ba 100644
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/CommentAttributesTest.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/CommentAttributesTest.php
@@ -19,7 +19,7 @@ class CommentAttributesTest extends CommentTestBase {
    *
    * @var array
    */
-  public static $modules = array('comment', 'rdf');
+  public static $modules = array('comment', 'rdf', 'rdf_test');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/CrudTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/CrudTest.php
index 3383f94..7a89158 100644
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/CrudTest.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/CrudTest.php
@@ -7,19 +7,19 @@
 
 namespace Drupal\rdf\Tests;
 
-use Drupal\simpletest\WebTestBase;
+use Drupal\simpletest\DrupalUnitTestBase;
 
 /**
  * Tests the RDF mapping CRUD functions.
  */
-class CrudTest extends WebTestBase {
+class CrudTest extends DrupalUnitTestBase {
 
   /**
    * Modules to enable.
    *
    * @var array
    */
-  public static $modules = array('rdf', 'rdf_test');
+  public static $modules = array('entity_test', 'rdf', 'system');
 
   public static function getInfo() {
     return array(
@@ -33,53 +33,57 @@ public static function getInfo() {
    * Tests inserting, loading, updating, and deleting RDF mappings.
    */
   function testCRUD() {
-    // Verify loading of a default mapping.
-    $mapping = _rdf_mapping_load('test_entity', 'test_bundle');
-    $this->assertTrue(count($mapping), 'Default mapping was found.');
+    $entity_type = $bundle = 'entity_test';
+    $mapping_manager = drupal_container()->get('rdf.mapping_manager');
+    $bundle_mapping_config_name = "rdf.mapping.bundle.syndication.$entity_type.$bundle";
+    $user_id_mapping_config_name = "rdf.mapping.field.syndication.$entity_type.$bundle.user_id";
 
-    // Verify saving a mapping.
-    $mapping = array(
-      'type' => 'crud_test_entity',
-      'bundle' => 'crud_test_bundle',
-      'mapping' => array(
-        'rdftype' => array('sioc:Post'),
-        'title' => array(
-          'predicates' => array('dc:title'),
-        ),
-        'uid' => array(
-          'predicates' => array('sioc:has_creator', 'dc:creator'),
-          'type' => 'rel',
-        ),
-      ),
-    );
-    $this->assertTrue(rdf_mapping_save($mapping) === SAVED_NEW, 'Mapping was saved.');
-
-    // Read the raw record from the {rdf_mapping} table.
-    $result = db_query('SELECT * FROM {rdf_mapping} WHERE type = :type AND bundle = :bundle', array(':type' => $mapping['type'], ':bundle' => $mapping['bundle']));
-    $stored_mapping = $result->fetchAssoc();
-    $stored_mapping['mapping'] = unserialize($stored_mapping['mapping']);
-    $this->assertEqual($mapping, $stored_mapping, 'Mapping was stored properly in the {rdf_mapping} table.');
+    // Save bundle mapping config.
+    $bundle_mapping = $mapping_manager->getBundleMappingConfig($entity_type, $bundle);
+    $bundle_mapping->set('types', array('sioc:Post'))->save();
+    // Save field mapping config.
+    $user_id_mapping = $mapping_manager->getFieldMappingConfig($entity_type, $bundle, 'user_id');
+    $user_id_mapping->set('properties', array('sioc:has_creator', 'dc:creator'))->save();
 
-    // Verify loading of saved mapping.
-    $this->assertEqual($mapping['mapping'], _rdf_mapping_load($mapping['type'], $mapping['bundle']), 'Saved mapping loaded successfully.');
+    // Test that config files were saved.
+    $bundle_mapping_configs = config_get_storage_names_with_prefix('rdf.mapping.bundle');
+    $this->assertTrue(in_array($bundle_mapping_config_name, $bundle_mapping_configs), 'Bundle mapping config saved.');
+    $field_mapping_configs = config_get_storage_names_with_prefix('rdf.mapping.field');
+    $this->assertTrue(in_array($user_id_mapping_config_name, $field_mapping_configs), 'Field mapping config saved.');
 
-    // Verify updating of mapping.
-    $mapping['mapping']['title'] = array(
-      'predicates' => array('dc2:bar2'),
-    );
-    $this->assertTrue(rdf_mapping_save($mapping) === SAVED_UPDATED, 'Mapping was updated.');
+    // Test that config can be loaded.
+    $loaded_bundle_config = $mapping_manager->getBundleMappingConfig($entity_type, $bundle);
+    $bundle_config_array = $loaded_bundle_config->get();
+    $this->assertTrue(!empty($bundle_config_array), 'Bundle mapping config loaded.');
+    $loaded_field_config = $mapping_manager->getFieldMappingConfig($entity_type, $bundle, 'user_id');
+    $field_config_array = $loaded_field_config->get();
+    $this->assertTrue(!empty($field_config_array), 'Field mapping config loaded.');
+    // Test that the proper IDs were set.
+    $this->assertEqual($bundle_config_array['mid'], "syndication.$entity_type.$bundle", 'Bundle mapping config has correct id.');
+    $this->assertEqual($field_config_array['mid'], "syndication.$entity_type.$bundle.user_id", 'Field mapping config has correct id.');
 
-    // Read the raw record from the {rdf_mapping} table.
-    $result = db_query('SELECT * FROM {rdf_mapping} WHERE type = :type AND bundle = :bundle', array(':type' => $mapping['type'], ':bundle' => $mapping['bundle']));
-    $stored_mapping = $result->fetchAssoc();
-    $stored_mapping['mapping'] = unserialize($stored_mapping['mapping']);
-    $this->assertEqual($mapping, $stored_mapping, 'Updated mapping was stored properly in the {rdf_mapping} table.');
+    // Test that the values were saved correctly.
+    $types = config($bundle_mapping_config_name)->get('types');
+    $this->assertTrue(in_array('sioc:Post', $types), 'Bundle mapping config values set properly.');
+    $properties = config($user_id_mapping_config_name)->get('properties');
+    $this->assertTrue(in_array('sioc:has_creator', $properties) && in_array('dc:creator', $properties), 'Field mapping config values set properly.');
 
-    // Verify loading of saved mapping.
-    $this->assertEqual($mapping['mapping'], _rdf_mapping_load($mapping['type'], $mapping['bundle']), 'Saved mapping loaded successfully.');
+    // Test that mapping can be updated.
+    $bundle_mapping->set('types', array('schema:Thing'))->save();
+    $loaded_bundle_config = $mapping_manager->getBundleMappingConfig($entity_type, $bundle);
+    $types = $loaded_bundle_config->get('types');
+    $this->assert(in_array('schema:Thing', $types) && !in_array('sioc:Post', $types), 'Bundle mapping was updated.');
+    $user_id_mapping->set('properties', array('schema:author'))->save();
+    $loaded_config = $mapping_manager->getFieldMappingConfig($entity_type, $bundle, 'user_id');
+    $properties = $loaded_config->get('properties');
+    $this->assert(in_array('schema:author', $properties) && !in_array('sioc:has_creator', $properties), 'Field mapping was updated.');
 
-    // Verify deleting of mapping.
-    $this->assertTrue(rdf_mapping_delete($mapping['type'], $mapping['bundle']), 'Mapping was deleted.');
-    $this->assertFalse(_rdf_mapping_load($mapping['type'], $mapping['bundle']), 'Deleted mapping is no longer found in the database.');
+    // Test that the mapping can be deleted.
+    $bundle_mapping->delete();
+    $bundle_mapping_configs = config_get_storage_names_with_prefix('rdf.mapping.bundle');
+    $this->assertFalse(in_array($bundle_mapping_config_name, $bundle_mapping_configs), 'Bundle mapping config deleted.');
+    $user_id_mapping->delete();
+    $field_mapping_configs = config_get_storage_names_with_prefix('rdf.mapping.field');
+    $this->assertFalse(in_array($user_id_mapping_config_name, $field_mapping_configs), 'Field mapping config deleted.');
   }
 }
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/GetNamespacesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/GetNamespacesTest.php
index a133230..0457ee4 100644
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/GetNamespacesTest.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/GetNamespacesTest.php
@@ -19,7 +19,7 @@ class GetNamespacesTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('rdf', 'rdf_test');
+  public static $modules = array('rdf', 'rdf_test_namespaces');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/GetRdfNamespacesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/GetRdfNamespacesTest.php
index 362a416..b2e881c 100644
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/GetRdfNamespacesTest.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/GetRdfNamespacesTest.php
@@ -19,7 +19,7 @@ class GetRdfNamespacesTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('rdf', 'rdf_test');
+  public static $modules = array('rdf', 'rdf_test_namespaces');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/MappingDefinitionTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/MappingDefinitionTest.php
deleted file mode 100644
index ef33e6f..0000000
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/MappingDefinitionTest.php
+++ /dev/null
@@ -1,135 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains Drupal\rdf\Tests\MappingDefinitionTest.
- */
-
-namespace Drupal\rdf\Tests;
-
-use Drupal\node\Tests\NodeTestBase;
-
-/**
- * Tests the RDF mapping definition functionality.
- */
-class MappingDefinitionTest extends NodeTestBase {
-
-  /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = array('rdf', 'rdf_test');
-
-  public static function getInfo() {
-    return array(
-      'name' => 'RDF mapping definition functionality',
-      'description' => 'Tests that RDF definitions are properly attached to entities.',
-      'group' => 'RDF',
-    );
-  }
-
-  function setUp() {
-    parent::setUp();
-    // NodeTestBase creates page content type for us.
-    // Defines RDF mapping for page content type.
-    $page_rdf_mapping = array(
-      'type' => 'node',
-      'bundle' => 'page',
-      'mapping' => array(
-        'rdftype' => array('foaf:DocumentBar'),
-        'body' => array(
-          'predicates' => array('dc:dummy-property'),
-        ),
-        'created' => array(
-          'predicates' => array('dc:dummy-date'),
-          'callback' => 'date_iso8601_foo',
-          'datatype' => 'xsd:dateTimeFoo',
-        ),
-      ),
-    );
-    rdf_mapping_save($page_rdf_mapping);
-  }
-
-  /**
-   * Creates a node of type page and tests whether the RDF mapping is
-   * attached to the node.
-   */
-  function testMappingDefinitionPage() {
-    $node = $this->drupalCreateNode(array('type' => 'page'));
-
-    $expected_mapping = array(
-      'rdftype' => array('foaf:DocumentBar'),
-      'title' => array(
-        'predicates' => array('dc:title'),
-      ),
-      'body' => array(
-        'predicates' => array('dc:dummy-property'),
-      ),
-      'created' => array(
-        'predicates' => array('dc:dummy-date'),
-        'callback' => 'date_iso8601_foo',
-        'datatype' => 'xsd:dateTimeFoo',
-      ),
-    );
-    $node = node_load($node->nid);
-    foreach ($expected_mapping as $key => $mapping) {
-      $this->assertEqual($node->rdf_mapping[$key], $mapping, format_string('Expected mapping found for @key.', array('@key' => $key)));
-    }
-  }
-
-  /**
-   * Creates a content type and a node of type test_bundle_hook_install and
-   * tests whether the RDF mapping defined in rdf_test.install is used.
-   */
-  function testMappingDefinitionTestBundleInstall() {
-    $this->drupalCreateContentType(array('type' => 'test_bundle_hook_install'));
-    $node = $this->drupalCreateNode(array('type' => 'test_bundle_hook_install'));
-
-    $expected_mapping = array(
-      'rdftype' => array('foo:mapping_install1', 'bar:mapping_install2'),
-      'title' => array(
-        'predicates' => array('dc:title'),
-      ),
-      'body' => array(
-        'predicates' => array('content:encoded'),
-      ),
-      'created' => array(
-        'predicates' => array('dc:date', 'dc:created'),
-        'callback' => 'date_iso8601',
-        'datatype' => 'xsd:dateTime',
-      ),
-    );
-    $node = node_load($node->nid);
-    foreach ($expected_mapping as $key => $mapping) {
-      $this->assertEqual($node->rdf_mapping[$key], $mapping, format_string('Expected mapping found for @key.', array('@key' => $key)));
-    }
-  }
-
-  /**
-   * Creates a random content type and node and ensures the default mapping for
-   * the node is being used.
-   */
-  function testMappingDefinitionRandomContentType() {
-    $type = $this->drupalCreateContentType();
-    $node = $this->drupalCreateNode(array('type' => $type->type));
-    $expected_mapping = array(
-      'rdftype' => array('sioc:Item', 'foaf:Document'),
-      'title' => array(
-        'predicates' => array('dc:title'),
-      ),
-      'body' => array(
-        'predicates' => array('content:encoded'),
-      ),
-      'created' => array(
-        'predicates' => array('dc:date', 'dc:created'),
-        'callback' => 'date_iso8601',
-        'datatype' => 'xsd:dateTime',
-      ),
-    );
-    $node = node_load($node->nid);
-    foreach ($expected_mapping as $key => $mapping) {
-      $this->assertEqual($node->rdf_mapping[$key], $mapping, format_string('Expected mapping found for @key.', array('@key' => $key)));
-    }
-  }
-}
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/MappingHookTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/MappingHookTest.php
deleted file mode 100644
index eb1340e..0000000
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/MappingHookTest.php
+++ /dev/null
@@ -1,50 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\rdf\Tests\MappingHookTest.
- */
-
-namespace Drupal\rdf\Tests;
-
-use Drupal\simpletest\WebTestBase;
-
-/**
- * Tests the RDF mapping hook.
- */
-class MappingHookTest extends WebTestBase {
-
-  /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = array('rdf', 'rdf_test', 'field_test');
-
-  public static function getInfo() {
-    return array(
-      'name' => 'RDF mapping hook',
-      'description' => 'Test hook_rdf_mapping().',
-      'group' => 'RDF',
-    );
-  }
-
-  /**
-   * Tests that hook_rdf_mapping() correctly returns and processes mapping.
-   */
-  function testMapping() {
-    // Test that the mapping is returned correctly by the hook.
-    $mapping = rdf_mapping_load('test_entity', 'test_bundle');
-    $this->assertIdentical($mapping['rdftype'], array('sioc:Post'), 'Mapping for rdftype is sioc:Post.');
-    $this->assertIdentical($mapping['title'], array('predicates' => array('dc:title')), 'Mapping for title is dc:title.');
-    $this->assertIdentical($mapping['created'], array(
-      'predicates' => array('dc:created'),
-      'datatype' => 'xsd:dateTime',
-      'callback' => 'date_iso8601',
-    ), 'Mapping for created is dc:created with datatype xsd:dateTime and callback date_iso8601.');
-    $this->assertIdentical($mapping['uid'], array('predicates' => array('sioc:has_creator', 'dc:creator'), 'type' => 'rel'), 'Mapping for uid is sioc:has_creator and dc:creator, and type is rel.');
-
-    $mapping = rdf_mapping_load('test_entity', 'test_bundle_no_mapping');
-    $this->assertEqual($mapping, array(), 'Empty array returned when an entity type, bundle pair has no mapping.');
-  }
-}
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/NodeAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/NodeAttributesTest.php
index e8df665..4e2bad2 100644
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/NodeAttributesTest.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/NodeAttributesTest.php
@@ -19,7 +19,7 @@ class NodeAttributesTest extends NodeTestBase {
    *
    * @var array
    */
-  public static $modules = array('rdf');
+  public static $modules = array('rdf', 'rdf_test');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/RdfMappingEventTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/RdfMappingEventTest.php
index 7a7a7b5..8ee12f8 100644
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/RdfMappingEventTest.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/RdfMappingEventTest.php
@@ -7,13 +7,13 @@
 namespace Drupal\rdf\Tests;
 
 use Drupal\Core\Cache\DatabaseBackend;
-use Drupal\rdf\RdfMappingManager;
+use Drupal\rdf\Mapping\RdfMappingManager;
 use Drupal\rdf\EventSubscriber\MappingSubscriber;
-use Drupal\rdf_test_mapping\EventSubscriber\TestMappingSubscriber;
+use Drupal\rdf_test_mapping_event\EventSubscriber\TestMappingSubscriber;
 use Drupal\rdf\SiteSchema\BundleSchema;
 use Drupal\rdf\SiteSchema\SiteSchema;
 use Drupal\rdf\SiteSchema\SiteSchemaManager;
-use Drupal\simpletest\WebTestBase;
+use Drupal\simpletest\DrupalUnitTestBase;
 use Symfony\Component\EventDispatcher\EventDispatcher;
 
 /**
@@ -21,14 +21,21 @@
  *
  * This is implemented as a WebTest because it depends on entity info.
  */
-class RdfMappingEventTest extends WebTestBase {
+class RdfMappingEventTest extends DrupalUnitTestBase {
 
   /**
    * Modules to enable.
    *
    * @var array
    */
-  public static $modules = array('rdf', 'rdf_test_mapping', 'entity_test');
+  public static $modules = array('system', 'rdf', 'rdf_test_mapping_event', 'entity_test');
+
+  /**
+   * The mock RDF mapping manager.
+   *
+   * @var \Drupal\rdf\Mapping\RdfMappingManager
+   */
+  protected $mappingManager;
 
   public static function getInfo() {
     return array(
@@ -39,26 +46,65 @@ public static function getInfo() {
   }
 
   /**
-   * Test that other modules can define incoming type mappings.
+   * Overrides \Drupal\simpletest\DrupalUnitTestBase::setUp().
    */
-  public function testMapInputType() {
+  public function setUp() {
+    parent::setUp();
+    $this->enableModules(array('rdf', 'rdf_test_mapping_event', 'system'));
+
+    // Set up the mock mapping manager.
     $dispatcher = new EventDispatcher();
     $dispatcher->addSubscriber(new MappingSubscriber());
     $dispatcher->addSubscriber(new TestMappingSubscriber());
     $site_schema_manager = new SiteSchemaManager(new DatabaseBackend('cache'));
-    $mapping_manager = new RdfMappingManager($dispatcher, $site_schema_manager);
+    $this->mappingManager = new RdfMappingManager($dispatcher, $site_schema_manager);
+  }
 
+  /**
+   * Test that other modules can define incoming type mappings.
+   */
+  public function testMapTypesFromInput() {
     // Test that a site schema URI is mapped to itself. This is the default
     // behavior.
     $schema = new SiteSchema(SiteSchema::CONTENT_DEPLOYMENT);
     $bundle_schema = $schema->bundle('entity_test', 'entity_test');
     $site_schema_type = $bundle_schema->getUri();
-    $typed_data_ids = $mapping_manager->getTypedDataIdsFromTypeUris(array($site_schema_type));
-    $this->assertTrue($typed_data_ids['bundle'] == 'entity_test', 'An internal site schema type URI is properly handled.');
+    $typed_data_ids = $this->mappingManager->getTypedDataIdsFromTypeUris(array($site_schema_type));
+    $this->assertTrue($typed_data_ids['bundle'] == 'entity_test', 'An internal site schema type URI is properly handled on input.');
 
     // Test that a module can map an external URI to a site schema URI.
-    $typed_data_ids = $mapping_manager->getTypedDataIdsFromTypeUris(array(TestMappingSubscriber::STAGING_SITE_TYPE_URI));
-    $this->assertTrue($typed_data_ids['bundle'] == 'entity_test', 'Modules can map external type URIs to a site schema type.');
+    $typed_data_ids = $this->mappingManager->getTypedDataIdsFromTypeUris(array(TestMappingSubscriber::STAGING_SITE_TYPE_URI));
+    $this->assertTrue($typed_data_ids['bundle'] == 'entity_test', 'Modules can map external type URIs to a site schema type on input.');
+  }
+
+  /**
+   * Test that other modules can define output mappings for bundles.
+   */
+  public function testMapBundleForOutput() {
+    $mapping = $this->mappingManager->getBundleMapping('entity_test', 'entity_test');
+
+    // Test that the site schema URI is automatically added in the mapping.
+    $schema = new SiteSchema(SiteSchema::SYNDICATION);
+    $bundle_schema = $schema->bundle('entity_test', 'entity_test');
+    $this->assertTrue(in_array($bundle_schema->getCurie(), $mapping['types']), 'Internal site schema type included in mapping on output.');
+
+    // Test that a module can add types to a mapping via an event.
+    $this->assertTrue(in_array(TestMappingSubscriber::STAGING_SITE_TYPE_URI, $mapping['types']), 'Modules can map site schema types to external type URIs on output.');
+  }
+
+  /**
+   * Test that other modules can define output mappings for fields.
+   */
+  public function testMapFieldForOutput() {
+    $mapping = $this->mappingManager->getFieldMapping('entity_test', 'entity_test', 'user_id');
+
+    // Test that the site schema URI is automatically added in the mapping.
+    $schema = new SiteSchema(SiteSchema::SYNDICATION);
+    $field_schema = $schema->field('entity_test', 'entity_test', 'user_id');
+    $this->assertTrue(in_array($field_schema->getCurie(), $mapping['properties']), 'Internal site schema property included in mapping on output.');
+
+    // Test that a module can add types to a mapping via an event.
+    $this->assertTrue(in_array(TestMappingSubscriber::STAGING_SITE_PROPERTY_URI, $mapping['properties']), 'Modules can map site schema types to external property URIs on output.');
   }
 
 }
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaMarkupTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaMarkupTest.php
index 6f99dae..78707e5 100644
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaMarkupTest.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaMarkupTest.php
@@ -7,7 +7,12 @@
 
 namespace Drupal\rdf\Tests;
 
+use Drupal\Core\Cache\DatabaseBackend;
+use Drupal\rdf\EventSubscriber\MappingSubscriber;
+use Drupal\rdf\Mapping\RdfMappingManager;
+use Drupal\rdf\SiteSchema\SiteSchemaManager;
 use Drupal\simpletest\WebTestBase;
+use Symfony\Component\EventDispatcher\EventDispatcher;
 
 /**
  * Tests RDFa markup generation.
@@ -19,10 +24,9 @@ class RdfaMarkupTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('rdf', 'field_test', 'rdf_test');
+  public static $modules = array('rdf', 'field_test');
 
   protected $profile = 'standard';
-
   public static function getInfo() {
     return array(
       'name' => 'RDFa markup',
@@ -32,63 +36,15 @@ public static function getInfo() {
   }
 
   /**
-   * Tests rdf_rdfa_attributes().
+   * Overrides \Drupal\simpletest\DrupalUnitTestBase::setUp().
    */
-  function testDrupalRdfaAttributes() {
-    // Same value as the one in the HTML tag (no callback function).
-    $expected_attributes = array(
-      'property' => array('dc:title'),
-    );
-    $mapping = rdf_mapping_load('test_entity', 'test_bundle');
-    $attributes = rdf_rdfa_attributes($mapping['title']);
-    ksort($expected_attributes);
-    ksort($attributes);
-    $this->assertEqual($expected_attributes, $attributes);
-
-    // Value different from the one in the HTML tag (callback function).
-    $date = 1252750327;
-    $isoDate = date('c', $date);
-    $expected_attributes = array(
-      'datatype' => 'xsd:dateTime',
-      'property' => array('dc:created'),
-      'content' => $isoDate,
-    );
-    $mapping = rdf_mapping_load('test_entity', 'test_bundle');
-    $attributes = rdf_rdfa_attributes($mapping['created'], $date);
-    ksort($expected_attributes);
-    ksort($attributes);
-    $this->assertEqual($expected_attributes, $attributes);
-
-    // Same value as the one in the HTML tag with datatype.
-    $expected_attributes = array(
-      'datatype' => 'foo:bar1type',
-      'property' => array('foo:bar1'),
-    );
-    $mapping = rdf_mapping_load('test_entity', 'test_bundle');
-    $attributes = rdf_rdfa_attributes($mapping['foobar1']);
-    ksort($expected_attributes);
-    ksort($attributes);
-    $this->assertEqual($expected_attributes, $attributes);
-
-    // ObjectProperty mapping (rel).
-    $expected_attributes = array(
-      'rel' => array('sioc:has_creator', 'dc:creator'),
-    );
-    $mapping = rdf_mapping_load('test_entity', 'test_bundle');
-    $attributes = rdf_rdfa_attributes($mapping['foobar_objproperty1']);
-    ksort($expected_attributes);
-    ksort($attributes);
-    $this->assertEqual($expected_attributes, $attributes);
-
-    // Inverse ObjectProperty mapping (rev).
-    $expected_attributes = array(
-      'rev' => array('sioc:reply_of'),
-    );
-    $mapping = rdf_mapping_load('test_entity', 'test_bundle');
-    $attributes = rdf_rdfa_attributes($mapping['foobar_objproperty2']);
-    ksort($expected_attributes);
-    ksort($attributes);
-    $this->assertEqual($expected_attributes, $attributes);
+  public function setUp() {
+    parent::setUp();
+    // Set up the mock mapping manager.
+    $dispatcher = new EventDispatcher();
+    $dispatcher->addSubscriber(new MappingSubscriber());
+    $site_schema_manager = new SiteSchemaManager(new DatabaseBackend('cache'));
+    $this->mappingManager = new RdfMappingManager($dispatcher, $site_schema_manager);
   }
 
   /**
@@ -124,10 +80,9 @@ function testAttributesInMarkupFile() {
       ->save();
 
     // Set the RDF mapping for the new field.
-    $rdf_mapping = rdf_mapping_load('node', $bundle_name);
-    $rdf_mapping += array($field_name => array('predicates' => array('rdfs:seeAlso'), 'type' => 'rel'));
-    $rdf_mapping_save = array('mapping' => $rdf_mapping, 'type' => 'node', 'bundle' => $bundle_name);
-    rdf_mapping_save($rdf_mapping_save);
+    $config = $this->mappingManager->getFieldMappingConfig('node', 'article', $field_name);
+    $field_mapping = array_merge($config->get(), array('properties' => array('rdfs:seeAlso'), 'mapping_type' => 'rel'));
+    $config->setData($field_mapping)->save();
 
     // Get the test file that simpletest provides.
     $file = current($this->drupalGetTestFiles('text'));
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/SiteSchemaTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/SiteSchemaTest.php
index 0cfe07f..b8d20ce 100644
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/SiteSchemaTest.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/SiteSchemaTest.php
@@ -44,7 +44,7 @@ function testSiteSchema() {
     $bundle_uri = url("$schema_path$entity_type/$bundle", array('absolute' => TRUE));
     $bundle_properties = array(
       'http://www.w3.org/2000/01/rdf-schema#isDefinedBy' => url($schema_path, 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/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("$schema_path$entity_type", array('absolute' => TRUE)),
     );
 
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyAttributesTest.php
index 83e6ec9..e3dd30b 100644
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyAttributesTest.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyAttributesTest.php
@@ -29,12 +29,37 @@ public static function getInfo() {
     );
   }
 
+  function setUp() {
+    parent::setUp();
+
+    // Create a default vocabulary named "Tags", enabled for the 'article' content type.
+    $description = st('Use tags to group articles on similar topics into categories.');
+    $help = st('Enter a comma-separated list of words to describe your content.');
+    $vocabulary = entity_create('taxonomy_vocabulary', array(
+      'name' => st('Tags'),
+      'description' => $description,
+      'vid' => 'tags',
+      'langcode' => language_default()->langcode,
+      'help' => $help,
+    ));
+    taxonomy_vocabulary_save($vocabulary);
+
+    // Configure RDF mapping for Tags bundle.
+    $rdf_mapping_manager = drupal_container()->get('rdf.mapping_manager');
+    $config = $rdf_mapping_manager->getBundleMappingConfig('taxonomy_term', 'tags');
+    $bundle_mapping = array_merge($config->get(), array('types' => array('skos:Concept')));
+    $config->setData($bundle_mapping)->save();
+    // Configure RDF mapping for name field on Tags bundle.
+    $config = $rdf_mapping_manager->getFieldMappingConfig('taxonomy_term', 'tags', 'name');
+    $field_mapping = array_merge($config->get(), array('properties' => array('rdfs:label', 'skos:prefLabel')));
+    $config->setData($field_mapping)->save();
+  }
+
   /**
    * Creates a random term and ensures the RDF output is correct.
    */
   function testTaxonomyTermRdfaAttributes() {
-    $vocabulary = $this->createVocabulary();
-    $term = $this->createTerm($vocabulary);
+    $term = $this->createTerm(taxonomy_vocabulary_load('tags'));
     $term_uri = url('taxonomy/term/' . $term->tid, array('absolute' => TRUE));
 
     // Parses the term's page and checks that the RDF output is correct.
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/TrackerAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/TrackerAttributesTest.php
index 45488bd..dd67950 100644
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/TrackerAttributesTest.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/TrackerAttributesTest.php
@@ -36,6 +36,54 @@ function setUp() {
     // Creates article content type.
     $this->drupalCreateContentType(array('type' => 'article', 'name' => t('Article')));
 
+    // Set bundle RDF mapping config for article.
+    $rdf_mapping_manager = drupal_container()->get('rdf.mapping_manager');
+    $config = $rdf_mapping_manager->getBundleMappingConfig('node', 'article');
+    $bundle_mapping = array_merge($config->get(), array('types' => array('foaf:Document', 'sioc:Item')));
+    $config->setData($bundle_mapping)->save();
+
+    // Set fields RDF mapping config for article.
+    $node_shared_field_mappings = array(
+      'title' => array(
+        'properties' => array('dc:title'),
+      ),
+      'created' => array(
+        'properties' => array('dc:date', 'dc:created'),
+        'datatype' => 'xsd:dateTime',
+        'datatype_callback' => 'date_iso8601',
+      ),
+      'changed' => array(
+        'properties' => array('dc:modified'),
+        'datatype' => 'xsd:dateTime',
+        'datatype_callback' => 'date_iso8601',
+      ),
+      'body' => array(
+        'properties' => array('content:encoded'),
+      ),
+      'uid' => array(
+        'properties' => array('sioc:has_creator'),
+        'mapping_type' => 'rel',
+      ),
+      'name' => array(
+        'properties' => array('foaf:name'),
+      ),
+      'comment_count' => array(
+        'properties' => array('sioc:num_replies'),
+        'datatype' => 'xsd:integer',
+      ),
+      'last_activity' => array(
+        'properties' => array('sioc:last_activity_date'),
+        'datatype' => 'xsd:dateTime',
+        'datatype_callback' => 'date_iso8601',
+      ),
+    );
+    // Iterate over field mappings and save.
+    foreach ($node_shared_field_mappings as $field_name => $field_mapping) {
+      $config = $rdf_mapping_manager->getFieldMappingConfig('node', 'article', $field_name);
+      $field_mapping = array_merge($config->get(), $field_mapping);
+      $config->setData($field_mapping)->save();
+    }
+
     // Enables anonymous posting of content.
     user_role_change_permissions(DRUPAL_ANONYMOUS_RID, array(
       'create article content' => TRUE,
diff --git a/core/modules/rdf/rdf.api.php b/core/modules/rdf/rdf.api.php
index b3f95ba..a1a7c18 100644
--- a/core/modules/rdf/rdf.api.php
+++ b/core/modules/rdf/rdf.api.php
@@ -68,11 +68,11 @@ function hook_rdf_mapping() {
         ),
         'pid' => array(
           'predicates' => array('sioc:reply_of'),
-          'type' => 'rel',
+          'mapping_type' => 'rel',
         ),
         'uid' => array(
           'predicates' => array('sioc:has_creator'),
-          'type' => 'rel',
+          'mapping_type' => 'rel',
         ),
         'name' => array(
           'predicates' => array('foaf:name'),
diff --git a/core/modules/rdf/rdf.install b/core/modules/rdf/rdf.install
index 10d3f8d..be2cde1 100644
--- a/core/modules/rdf/rdf.install
+++ b/core/modules/rdf/rdf.install
@@ -4,46 +4,3 @@
  * @file
  * Install, update and uninstall functions for the rdf module.
  */
-
-/**
- * Implements hook_schema().
- */
-function rdf_schema() {
-  $schema['rdf_mapping'] = array(
-    'description' => 'Stores custom RDF mappings for user defined content types or overriden module-defined mappings',
-    'fields' => array(
-      'type' => array(
-        'type' => 'varchar',
-        'length' => 128,
-        'not null' => TRUE,
-        'description' => 'The name of the entity type a mapping applies to (node, user, comment, etc.).',
-      ),
-      'bundle' => array(
-        'type' => 'varchar',
-        'length' => 128,
-        'not null' => TRUE,
-        'description' => 'The name of the bundle a mapping applies to.',
-      ),
-      'mapping' => array(
-        'description' => 'The serialized mapping of the bundle type and fields to RDF terms.',
-        'type' => 'blob',
-        'not null' => FALSE,
-        'size' => 'big',
-        'serialize' => TRUE,
-      ),
-    ),
-    'primary key' => array('type', 'bundle'),
-  );
-
-  return $schema;
-}
-
-/**
- * Implements hook_install().
- */
-function rdf_install() {
-  // Collect any RDF mappings that were declared by modules installed before
-  // this one.
-  $modules = module_implements('rdf_mapping');
-  rdf_modules_installed($modules);
-}
diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module
index 89494eb..a729a5f 100644
--- a/core/modules/rdf/rdf.module
+++ b/core/modules/rdf/rdf.module
@@ -43,23 +43,23 @@ function rdf_help($path, $arg) {
  *     'type' => 'node',
  *     'bundle' => RDF_DEFAULT_BUNDLE,
  *     'mapping' => array(
- *       'rdftype' => array('sioc:Item', 'foaf:Document'),
+ *       'types' => array('sioc:Item', 'foaf:Document'),
  *       'title' => array(
- *         'predicates' => array('dc:title'),
+ *         'properties' => array('dc:title'),
  *       ),
  *       'created' => array(
- *         'predicates' => array('dc:date', 'dc:created'),
+ *         'properties' => array('dc:date', 'dc:created'),
  *         'datatype' => 'xsd:dateTime',
- *         'callback' => 'date_iso8601',
+ *         'datatype_callback' => 'date_iso8601',
  *       ),
  *      'body' => array(
- *         'predicates' => array('content:encoded'),
+ *         'properties' => array('content:encoded'),
  *       ),
  *       'uid' => array(
- *         'predicates' => array('sioc:has_creator'),
+ *         'properties' => array('sioc:has_creator'),
  *       ),
  *       'name' => array(
- *         'predicates' => array('foaf:name'),
+ *         'properties' => array('foaf:name'),
  *       ),
  *     ),
  *   );
@@ -67,16 +67,6 @@ function rdf_help($path, $arg) {
  */
 
 /**
- * RDF bundle flag: Default bundle.
- *
- * Implementations of hook_rdf_mapping() should use this constant for the
- * 'bundle' key when defining a default set of RDF mappings for an entity type.
- * Each bundle will inherit the default mappings defined for the entity type
- * unless the bundle defines its own specific mappings.
- */
-const RDF_DEFAULT_BUNDLE = '';
-
-/**
  * Implements hook_rdf_namespaces().
  */
 function rdf_rdf_namespaces() {
@@ -113,167 +103,15 @@ function rdf_get_namespaces() {
 }
 
 /**
- * Returns the mapping for attributes of a given entity type/bundle pair.
- *
- * @param $type
- *   An entity type.
- * @param $bundle
- *   (optional) A bundle name.
- *
- * @return
- *   The mapping corresponding to the requested entity type/bundle pair or an
- *   empty array.
- */
-function rdf_mapping_load($type, $bundle = RDF_DEFAULT_BUNDLE) {
-  // Retrieves the bundle-specific mapping from the entity info.
-  $bundles = entity_get_bundles($type);
-  if (!empty($bundles[$bundle]['rdf_mapping'])) {
-    return $bundles[$bundle]['rdf_mapping'];
-  }
-  // If there is no mapping defined for this bundle, we return the default
-  // mapping that is defined for this entity type.
-  else {
-    return _rdf_get_default_mapping($type);
-  }
-}
-
-/**
  * @} End of "defgroup rdf".
  */
 
 /**
- * Gets the default RDF mapping for a given entity type.
- *
- * @param $type
- *   An entity type, e.g. 'node' or 'comment'.
- *
- * @return
- *   The RDF mapping or an empty array if no mapping is defined for this entity
- *   type.
- */
-function _rdf_get_default_mapping($type) {
-  $default_mappings = &drupal_static(__FUNCTION__);
-
-  if (!isset($default_mappings)) {
-    // Get all of the modules that implement hook_rdf_mapping().
-    $modules = module_implements('rdf_mapping');
-
-    // Only consider the default entity mapping definitions.
-    foreach ($modules as $module) {
-      $mappings = module_invoke($module, 'rdf_mapping');
-      foreach ($mappings as $mapping) {
-        if ($mapping['bundle'] === RDF_DEFAULT_BUNDLE) {
-          $default_mappings[$mapping['type']] = $mapping['mapping'];
-        }
-      }
-    }
-  }
-
-  return isset($default_mappings[$type]) ? $default_mappings[$type] : array();
-}
-
-/**
- * Retrieves an RDF mapping from the database.
- *
- * @param $type
- *   The entity type the mapping refers to.
- * @param $bundle
- *   The bundle the mapping refers to.
- *
- * @return
- *   An RDF mapping structure or, FALSE if the mapping does not exist.
- */
-function _rdf_mapping_load($type, $bundle) {
-  $mappings = _rdf_mapping_load_multiple($type, array($bundle));
-  return $mappings ? reset($mappings) : FALSE;
-}
-
-/**
- * Helper function to retrieve a set of RDF mappings from the database.
- *
- * @param $type
- *   The entity type of the mappings.
- * @param $bundles
- *   The bundles the mappings refer to.
- *
- * @return
- *   An array of RDF mapping structures, or an empty array.
- */
-function _rdf_mapping_load_multiple($type, array $bundles) {
-  $mappings = db_select('rdf_mapping')
-    ->fields(NULL, array('bundle', 'mapping'))
-    ->condition('type', $type)
-    ->condition('bundle', $bundles)
-    ->execute()
-    ->fetchAllKeyed();
-
-  foreach ($mappings as $bundle => $mapping) {
-    $mappings[$bundle] = unserialize($mapping);
-  }
-  return $mappings;
-}
-
-/**
  * @addtogroup rdf
  * @{
  */
 
 /**
- * Saves an RDF mapping to the database.
- *
- * Takes a mapping structure returned by hook_rdf_mapping() implementations
- * and creates or updates a record mapping for each encountered entity
- * type/bundle pair. If available, adds default values for non-existent mapping
- * keys.
- *
- * @param $mapping
- *   The RDF mapping to save.
- *
- * @return
- *   MergeQuery object that indicates the outcome of the operation.
- */
-function rdf_mapping_save($mapping) {
-  // In the case where a field has a mapping defined in the default entity
-  // mapping, but a mapping is not specified in the bundle-specific mapping,
-  // then use the default mapping for that field.
-  $mapping['mapping'] += _rdf_get_default_mapping($mapping['type']);
-
-  $status = db_merge('rdf_mapping')
-    ->key(array(
-      'type' => $mapping['type'],
-      'bundle' => $mapping['bundle'],
-    ))
-    ->fields(array(
-      'mapping' => serialize($mapping['mapping']),
-    ))
-    ->execute();
-
-  entity_info_cache_clear();
-
-  return $status;
-}
-
-/**
- * Deletes the mapping for the given bundle from the database.
- *
- * @param $type
- *   The entity type the mapping refers to.
- * @param $bundle
- *   The bundle the mapping refers to.
- *
- * @return
- *   TRUE if the mapping is deleted, FALSE if not.
- */
-function rdf_mapping_delete($type, $bundle) {
-  $num_rows = db_delete('rdf_mapping')
-    ->condition('type', $type)
-    ->condition('bundle', $bundle)
-    ->execute();
-
-  return (bool) ($num_rows > 0);
-}
-
-/**
  * Builds an array of RDFa attributes for a given mapping.
  *
  * This array will typically be passed through Drupal\Core\Template\Attribute
@@ -282,13 +120,13 @@ function rdf_mapping_delete($type, $bundle) {
  * field-specific $item_attributes variables.
  *
  * @param $mapping
- *   An array containing a mandatory 'predicates' key and optional 'datatype',
- *   'callback' and 'type' keys. For example:
+ *   An array containing a mandatory 'properties' key and optional 'datatype',
+ *   'datatype_callback' and 'type' keys. For example:
  *   @code
  *     array(
- *       'predicates' => array('dc:created'),
+ *       'properties' => array('dc:created'),
  *       'datatype' => 'xsd:dateTime',
- *       'callback' => 'date_iso8601',
+ *       'datatype_callback' => 'date_iso8601',
  *       ),
  *     );
  *   @endcode
@@ -303,22 +141,22 @@ function rdf_mapping_delete($type, $bundle) {
  */
 function rdf_rdfa_attributes($mapping, $data = NULL) {
   // The type of mapping defaults to 'property'.
-  $type = isset($mapping['type']) ? $mapping['type'] : 'property';
+  $type = isset($mapping['mapping_type']) ? $mapping['mapping_type'] : 'property';
 
   switch ($type) {
     // The mapping expresses the relationship between two resources.
     case 'rel':
     case 'rev':
-      $attributes[$type] = $mapping['predicates'];
+      $attributes[$type] = $mapping['properties'];
       break;
 
     // The mapping expresses the relationship between a resource and some
     // literal text.
     case 'property':
-      $attributes['property'] = $mapping['predicates'];
+      $attributes['property'] = $mapping['properties'];
       // Convert $data to a specific format as per the callback function.
-      if (isset($data) && isset($mapping['callback'])) {
-        $callback = $mapping['callback'];
+      if (isset($data) && isset($mapping['datatype_callback'])) {
+        $callback = $mapping['datatype_callback'];
         $attributes['content'] = $callback($data);
       }
       if (isset($mapping['datatype'])) {
@@ -335,88 +173,18 @@ function rdf_rdfa_attributes($mapping, $data = NULL) {
  */
 
 /**
- * Implements hook_modules_installed().
- *
- * Checks if the installed modules have any RDF mapping definitions to declare
- * and stores them in the rdf_mapping table.
- *
- * While both default entity mappings and specific bundle mappings can be
- * defined in hook_rdf_mapping(), default entity mappings are not stored in the
- * database. Only overridden mappings are stored in the database. The default
- * entity mappings can be overriden by specific bundle mappings which are
- * stored in the database and can be altered via the RDF CRUD mapping API.
- */
-function rdf_modules_installed($modules) {
-  foreach ($modules as $module) {
-    $function = $module . '_rdf_mapping';
-    if (function_exists($function)) {
-      foreach ($function() as $mapping) {
-        // Only the bundle mappings are saved in the database.
-        if ($mapping['bundle'] !== RDF_DEFAULT_BUNDLE) {
-          rdf_mapping_save($mapping);
-        }
-      }
-    }
-  }
-}
-
-/**
- * Implements hook_modules_uninstalled().
- */
-function rdf_modules_uninstalled($modules) {
-  // @todo Remove RDF mappings of uninstalled modules.
-}
-
-/**
- * Implements hook_entity_bundle_info_alter().
- *
- * Adds the proper RDF mapping to each entity type/bundle pair.
- *
- * @todo May need to move the comment below to another place.
- * This hook should not be used by modules to alter the bundle mappings. The UI
- * should always be authoritative. UI mappings are stored in the database and
- * if hook_entity_info_alter() was used to override module defined mappings, it
- * would override the user defined mapping as well.
- *
- */
-function rdf_entity_bundle_info_alter(&$bundles) {
-  // Loop through each entity type and its bundles.
-  foreach ($bundles as $entity_type => $bundle_info) {
-    $bundle_names = array_keys($bundle_info);
-    $mappings = _rdf_mapping_load_multiple($entity_type, $bundle_names);
-
-    foreach ($bundle_names as $bundle) {
-      if (isset($mappings[$bundle])) {
-        $bundles[$entity_type][$bundle]['rdf_mapping'] = $mappings[$bundle];
-      }
-      else {
-        // If no mapping was found in the database, assign the default RDF
-        // mapping for this entity type.
-        $bundles[$entity_type][$bundle]['rdf_mapping'] = _rdf_get_default_mapping($entity_type);
-      }
-    }
-  }
-}
-
-/**
- * Implements hook_entity_load().
- */
-function rdf_entity_load($entities, $type) {
-  foreach ($entities as $entity) {
-    // Extracts the bundle of the entity being loaded.
-    $entity->rdf_mapping = rdf_mapping_load($type, $entity->bundle());
-  }
-}
-
-/**
  * Implements hook_comment_load().
+ *
+ * @todo refactor to take advantage of the new Drupal 8 APIs.
  */
 function rdf_comment_load($comments) {
   foreach ($comments as $comment) {
     // Pages with many comments can show poor performance. This information
     // isn't needed until rdf_preprocess_comment() is called, but set it here
     // to optimize performance for websites that implement an entity cache.
-    $comment->rdf_data['date'] = rdf_rdfa_attributes($comment->rdf_mapping['created'], $comment->created->value);
+    $mapping_manager = drupal_container()->get('rdf.mapping_manager');
+    $created_mapping = $mapping_manager->getFieldMapping('comment', $comment->bundle(), 'created');
+    $comment->rdf_data['date'] = rdf_rdfa_attributes($created_mapping, $comment->created->value);
     $comment->rdf_data['nid_uri'] = url('node/' . $comment->nid->target_id);
     if ($comment->pid->target_id) {
       $comment->rdf_data['pid_uri'] = url('comment/' . $comment->pid->target_id, array('fragment' => 'comment-' . $comment->pid->target_id));
@@ -499,14 +267,17 @@ function rdf_preprocess_node(&$variables) {
   // URI of the resource described within the HTML element, while the @typeof
   // attribute indicates its RDF type (e.g., foaf:Document, sioc:Person, and so
   // on.)
+  $mapping_manager = drupal_container()->get('rdf.mapping_manager');
+  $bundle_mapping = $mapping_manager->getBundleMapping('node', $variables['type']);
   $variables['attributes']['about'] = empty($variables['node_url']) ? NULL: $variables['node_url'];
-  $variables['attributes']['typeof'] = empty($variables['node']->rdf_mapping['rdftype']) ? NULL : $variables['node']->rdf_mapping['rdftype'];
+  $variables['attributes']['typeof'] = empty($bundle_mapping['types']) ? NULL : $bundle_mapping['types'];
 
   // Adds RDFa markup to the title of the node. Because the RDFa markup is
   // added to the <h2> tag which might contain HTML code, we specify an empty
   // datatype to ensure the value of the title read by the RDFa parsers is a
   // literal.
-  $variables['title_attributes']['property'] = empty($variables['node']->rdf_mapping['title']['predicates']) ? NULL : $variables['node']->rdf_mapping['title']['predicates'];
+  $title_mapping = $mapping_manager->getFieldMapping('node', $variables['type'], 'title');
+  $variables['title_attributes']['property'] = empty($title_mapping['properties']) ? NULL : $title_mapping['properties'];
   $variables['title_attributes']['datatype'] = '';
 
   // In full node mode, the title is not displayed by node.tpl.php so it is
@@ -519,35 +290,38 @@ function rdf_preprocess_node(&$variables) {
         'about' => $variables['node_url'],
       ),
     );
-    if (!empty($variables['node']->rdf_mapping['title']['predicates'])) {
-      $element['#attributes']['property'] = $variables['node']->rdf_mapping['title']['predicates'];
+    if (!empty($title_mapping['properties'])) {
+      $element['#attributes']['property'] = $title_mapping['properties'];
     }
     drupal_add_html_head($element, 'rdf_node_title');
   }
 
   // Adds RDFa markup for the date.
-  if (!empty($variables['node']->rdf_mapping['created'])) {
-    $date_attributes = rdf_rdfa_attributes($variables['node']->rdf_mapping['created'], $variables['node']->created);
+  $created_mapping = $mapping_manager->getFieldMapping('node', $variables['type'], 'created');
+  if (!empty($created_mapping)) {
+    $date_attributes = rdf_rdfa_attributes($created_mapping, $variables['node']->created);
     $variables['rdf_template_variable_attributes']['date'] = $date_attributes;
     if ($variables['submitted']) {
       $variables['rdf_template_variable_attributes']['submitted'] = $date_attributes;
     }
   }
   // Adds RDFa markup for the relation between the node and its author.
-  if (!empty($variables['node']->rdf_mapping['uid'])) {
-    $variables['rdf_template_variable_attributes']['name']['rel'] = $variables['node']->rdf_mapping['uid']['predicates'];
+  $uid_mapping = $mapping_manager->getFieldMapping('node', $variables['type'], 'uid');
+  if (!empty($uid_mapping)) {
+    $variables['rdf_template_variable_attributes']['name']['rel'] = $uid_mapping['properties'];
     if ($variables['submitted']) {
-      $variables['rdf_template_variable_attributes']['submitted']['rel'] = $variables['node']->rdf_mapping['uid']['predicates'];
+      $variables['rdf_template_variable_attributes']['submitted']['rel'] = $uid_mapping['properties'];
     }
   }
 
   // Adds RDFa markup annotating the number of comments a node has.
-  if (isset($variables['node']->comment_count) && !empty($variables['node']->rdf_mapping['comment_count']['predicates'])) {
+  $comment_count_mapping = $mapping_manager->getFieldMapping('node', $variables['type'], 'comment_count');
+  if (isset($variables['node']->comment_count) && !empty($comment_count_mapping['properties'])) {
     // Annotates the 'x comments' link in teaser view.
     if (isset($variables['content']['links']['comment']['#links']['comment-comments'])) {
-      $comment_count_attributes['property'] = $variables['node']->rdf_mapping['comment_count']['predicates'];
+      $comment_count_attributes['property'] = $comment_count_mapping['properties'];
       $comment_count_attributes['content'] = $variables['node']->comment_count;
-      $comment_count_attributes['datatype'] = $variables['node']->rdf_mapping['comment_count']['datatype'];
+      $comment_count_attributes['datatype'] = $comment_count_mapping['datatype'];
       // According to RDFa parsing rule number 4, a new subject URI is created
       // from the href attribute if no rel/rev attribute is present. To get the
       // original node URL from the about attribute of the parent container we
@@ -564,9 +338,9 @@ function rdf_preprocess_node(&$variables) {
         '#tag' => 'meta',
         '#attributes' => array(
           'about' => $variables['node_url'],
-          'property' => $variables['node']->rdf_mapping['comment_count']['predicates'],
+          'property' => $comment_count_mapping['properties'],
           'content' => $variables['node']->comment_count,
-          'datatype' => $variables['node']->rdf_mapping['comment_count']['datatype'],
+          'datatype' => isset($comment_count_mapping['datatype']) ? $comment_count_mapping['datatype'] : NULL,
         ),
       );
       drupal_add_html_head($element, 'rdf_node_comment_count');
@@ -578,13 +352,16 @@ function rdf_preprocess_node(&$variables) {
  * Implements hook_preprocess_HOOK() for field.tpl.php.
  */
 function rdf_preprocess_field(&$variables) {
+  $mapping_manager = drupal_container()->get('rdf.mapping_manager');
   $element = $variables['element'];
-  $mapping = rdf_mapping_load($element['#entity_type'], $element['#bundle']);
+  $entity_type = $element['#entity_type'];
+  $bundle = $element['#bundle'];
   $field_name = $element['#field_name'];
+  $field_mapping = $mapping_manager->getFieldMapping($entity_type, $bundle, $field_name);
 
-  if (!empty($mapping) && !empty($mapping[$field_name])) {
+  if (!empty($field_mapping)) {
     foreach ($element['#items'] as $delta => $item) {
-      $variables['item_attributes'][$delta] = rdf_rdfa_attributes($mapping[$field_name], $item);
+      $variables['item_attributes'][$delta] = rdf_rdfa_attributes($field_mapping, $item);
       // If this field is an image, RDFa will not output correctly when the
       // image is in a containing <a> tag. If the field is a file, RDFa will
       // not output correctly if the filetype icon comes before the link to the
@@ -608,11 +385,13 @@ function rdf_preprocess_field(&$variables) {
 function rdf_preprocess_user(&$variables) {
   $account = $variables['elements']['#user'];
   $uri = $account->uri();
+  $mapping_manager = drupal_container()->get('rdf.mapping_manager');
+  $bundle_mapping = $mapping_manager->getBundleMapping('user', 'user');
 
   // Adds RDFa markup to the user profile page. Fields displayed in this page
   // will automatically describe the user.
-  if (!empty($account->rdf_mapping['rdftype'])) {
-    $variables['attributes']['typeof'] = $account->rdf_mapping['rdftype'];
+  if (!empty($bundle_mapping['types'])) {
+    $variables['attributes']['typeof'] = $bundle_mapping['types'];
     $variables['attributes']['about'] = url($uri['path'], $uri['options']);
   }
   // If we are on the user account page, add the relationship between the
@@ -629,11 +408,12 @@ function rdf_preprocess_user(&$variables) {
     );
     // Adds the markup for username as language neutral literal, see
     // rdf_preprocess_username().
+    $name_mapping = $mapping_manager->getFieldMapping('user', 'user', 'name');
     $username_meta = array(
       '#tag' => 'meta',
       '#attributes' => array(
         'about' => url($uri['path'], $uri['options']),
-        'property' => $account->rdf_mapping['name']['predicates'],
+        'property' => $name_mapping['properties'],
         'content' => $account->name,
         'lang' => '',
       )
@@ -656,16 +436,8 @@ function rdf_preprocess_username(&$variables) {
     $variables['attributes']['lang'] = '';
   }
 
-  // $variables['account'] is a pseudo account object, and as such, does not
-  // contain the RDF mappings for the user. In the case of nodes and comments,
-  // it contains the mappings for the node or comment object instead. However,
-  // while the RDF mappings are available from a full user_load(), this should
-  // be avoided for performance reasons. Since the type and bundle for users is
-  // already known, call rdf_mapping_load() directly.
-  $rdf_mapping = rdf_mapping_load('user', 'user');
-
   // The profile URI is used to identify the user account. The about attribute
-  // is used to set the URI as the default subject of the predicates embedded
+  // is used to set the URI as the default subject of the properties embedded
   // as RDFa in the child elements. Even if the user profile is not accessible
   // to the current user, we use its URI in order to identify the user in RDF.
   // We do not use this attribute for the anonymous user because we do not have
@@ -676,21 +448,24 @@ function rdf_preprocess_username(&$variables) {
   }
 
   $attributes = array();
-  // The typeof attribute specifies the RDF type(s) of this resource. They
-  // are defined in the 'rdftype' property of the user RDF mapping.
-  if (!empty($rdf_mapping['rdftype'])) {
-    $attributes['typeof'] = $rdf_mapping['rdftype'];
+  // Add RDF type of user.
+  $mapping_manager = drupal_container()->get('rdf.mapping_manager');
+  $bundle_mapping = $mapping_manager->getBundleMapping('user', 'user');
+  if (!empty($bundle_mapping['types'])) {
+    $attributes['typeof'] = $bundle_mapping['types'];
   }
   // Annotate the username in RDFa. A property attribute is used with an empty
   // datatype attribute to ensure the username is parsed as a plain literal
   // in RDFa 1.0 and 1.1.
-  if (!empty($rdf_mapping['name'])) {
-    $attributes['property'] = $rdf_mapping['name']['predicates'];
+  $name_mapping = $mapping_manager->getFieldMapping('user', 'user', 'name');
+  if (!empty($name_mapping)) {
+    $attributes['property'] = $name_mapping['properties'];
     $attributes['datatype'] = '';
   }
   // Add the homepage RDFa markup if present.
-  if (!empty($variables['homepage']) && !empty($rdf_mapping['homepage'])) {
-    $attributes['rel'] = $rdf_mapping['homepage']['predicates'];
+  $homepage_mapping = $mapping_manager->getFieldMapping('user', 'user', 'homepage');
+  if (!empty($variables['homepage']) && !empty($homepage_mapping)) {
+    $attributes['rel'] = $homepage_mapping['properties'];
   }
   // The remaining attributes can have multiple values listed, with whitespace
   // separating the values in the RDFa attributes
@@ -705,18 +480,22 @@ function rdf_preprocess_username(&$variables) {
  */
 function rdf_preprocess_comment(&$variables) {
   $comment = $variables['comment'];
-  if (!empty($comment->rdf_mapping['rdftype'])) {
+  $mapping_manager = drupal_container()->get('rdf.mapping_manager');
+  $bundle_mapping = $mapping_manager->getBundleMapping('comment', $comment->bundle());
+
+  if (!empty($bundle_mapping['types'])) {
     // Adds RDFa markup to the comment container. The about attribute specifies
     // the URI of the resource described within the HTML element, while the
     // typeof attribute indicates its RDF type (e.g., sioc:Post, foaf:Document,
     // and so on.)
     $uri = $comment->uri();
     $variables['attributes']['about'] = url($uri['path'], $uri['options']);
-    $variables['attributes']['typeof'] = $comment->rdf_mapping['rdftype'];
+    $variables['attributes']['typeof'] = $bundle_mapping['types'];
   }
 
   // Adds RDFa markup for the date of the comment.
-  if (!empty($comment->rdf_mapping['created'])) {
+  $created_mapping = $mapping_manager->getFieldMapping('comment', $comment->bundle(), 'created');
+  if (!empty($created_mapping)) {
     // The comment date is precomputed as part of the rdf_data so that it can be
     // cached as part of the entity.
     $date_attributes = $comment->rdf_data['date'];
@@ -724,24 +503,27 @@ function rdf_preprocess_comment(&$variables) {
     $variables['rdf_template_variable_attributes']['submitted'] = $date_attributes;
   }
   // Adds RDFa markup for the relation between the comment and its author.
-  if (!empty($comment->rdf_mapping['uid'])) {
-    $variables['rdf_template_variable_attributes']['author']['rel'] = $comment->rdf_mapping['uid']['predicates'];
-    $variables['rdf_template_variable_attributes']['submitted']['rel'] = $comment->rdf_mapping['uid']['predicates'];
+  $uid_mapping = $mapping_manager->getFieldMapping('comment', $comment->bundle(), 'uid');
+  if (!empty($uid_mapping)) {
+    $variables['rdf_template_variable_attributes']['author']['rel'] = $uid_mapping['properties'];
+    $variables['rdf_template_variable_attributes']['submitted']['rel'] = $uid_mapping['properties'];
   }
-  if (!empty($comment->rdf_mapping['title'])) {
+  $title_mapping = $mapping_manager->getFieldMapping('comment', $comment->bundle(), 'title');
+  if (!empty($title_mapping)) {
     // Adds RDFa markup to the subject of the comment. Because the RDFa markup
     // is added to an <h3> tag which might contain HTML code, we specify an
     // empty datatype to ensure the value of the title read by the RDFa parsers
     // is a literal.
-    $variables['title_attributes']['property'] = $comment->rdf_mapping['title']['predicates'];
+    $variables['title_attributes']['property'] = $title_mapping['properties'];
     $variables['title_attributes']['datatype'] = '';
   }
 
   // Annotates the parent relationship between the current comment and the node
   // it belongs to. If available, the parent comment is also annotated.
-  if (!empty($comment->rdf_mapping['pid'])) {
+  $pid_mapping = $mapping_manager->getFieldMapping('comment', $comment->bundle(), 'pid');
+  if (!empty($pid_mapping)) {
     // Adds the relation to the parent node.
-    $parent_node_attributes['rel'] = $comment->rdf_mapping['pid']['predicates'];
+    $parent_node_attributes['rel'] = $pid_mapping['properties'];
     // The parent node URI is precomputed as part of the rdf_data so that it can
     // be cached as part of the entity.
     $parent_node_attributes['resource'] = $comment->rdf_data['nid_uri'];
@@ -749,7 +531,7 @@ function rdf_preprocess_comment(&$variables) {
 
     // Adds the relation to parent comment, if it exists.
     if ($comment->pid->target_id != 0) {
-      $parent_comment_attributes['rel'] = $comment->rdf_mapping['pid']['predicates'];
+      $parent_comment_attributes['rel'] = $pid_mapping['properties'];
       // The parent comment URI is precomputed as part of the rdf_data so that
       // it can be cached as part of the entity.
       $parent_comment_attributes['resource'] = $comment->rdf_data['pid_uri'];
@@ -762,14 +544,17 @@ function rdf_preprocess_comment(&$variables) {
  * Implements hook_preprocess_HOOK() for taxonomy-term.tpl.php.
  */
 function rdf_preprocess_taxonomy_term(&$variables) {
-  // Adds the RDF type of the term and the term name in a <meta> tag.
   $term = $variables['term'];
+  $mapping_manager = drupal_container()->get('rdf.mapping_manager');
+  $bundle_mapping = $mapping_manager->getBundleMapping('taxonomy_term', $term->vid);
+  $name_field_mapping = $mapping_manager->getFieldMapping('taxonomy_term', $term->vid, 'name');
+  // Adds the RDF type of the term and the term name in a <meta> tag.
   $term_label_meta = array(
       '#tag' => 'meta',
       '#attributes' => array(
         'about' => url('taxonomy/term/' . $term->tid),
-        'typeof' => $term->rdf_mapping['rdftype'],
-        'property' => $term->rdf_mapping['name']['predicates'],
+        'typeof' => $bundle_mapping['types'],
+        'property' => $name_field_mapping['properties'],
         'content' => $term->label(),
       ),
     );
@@ -790,13 +575,16 @@ function rdf_field_attach_view_alter(&$output, $context) {
         // yet and thus have no $item['taxonomy_term'].
         if (isset($item['taxonomy_term'])) {
           $term = $item['taxonomy_term'];
-          if (!empty($term->rdf_mapping['rdftype'])) {
-            $element[$delta]['#options']['attributes']['typeof'] = $term->rdf_mapping['rdftype'];
+          $mapping_manager = drupal_container()->get('rdf.mapping_manager');
+          $bundle_mapping = $mapping_manager->getBundleMapping('taxonomy_term', $term->vid);
+          if (!empty($bundle_mapping['types'])) {
+            $element[$delta]['#options']['attributes']['typeof'] = $bundle_mapping['types'];
           }
-          if (!empty($term->rdf_mapping['name']['predicates'])) {
+          $name_field_mapping = $mapping_manager->getFieldMapping('taxonomy_term', $term->vid, 'name');
+          if (!empty($name_field_mapping['properties'])) {
             // A property attribute is used with an empty datatype attribute so
             // the term name is parsed as a plain literal in RDFa 1.0 and 1.1.
-            $element[$delta]['#options']['attributes']['property'] = $term->rdf_mapping['name']['predicates'];
+            $element[$delta]['#options']['attributes']['property'] = $name_field_mapping['properties'];
             $element[$delta]['#options']['attributes']['datatype'] = '';
           }
         }
diff --git a/core/modules/rdf/tests/rdf_test.install b/core/modules/rdf/tests/rdf_test.install
index 91a3392..9a4d76a 100644
--- a/core/modules/rdf/tests/rdf_test.install
+++ b/core/modules/rdf/tests/rdf_test.install
@@ -9,17 +9,108 @@
  * Implements hook_install().
  */
 function rdf_test_install() {
-  $rdf_mappings = array(
-    array(
-      'type' => 'node',
-      'bundle' => 'test_bundle_hook_install',
-      'mapping' => array(
-        'rdftype' => array('foo:mapping_install1', 'bar:mapping_install2'),
-      ),
+  // Set bundle mapping config.
+  $rdf_mapping_manager = drupal_container()->get('rdf.mapping_manager');
+  $config = $rdf_mapping_manager->getBundleMappingConfig('node', 'article');
+  $bundle_mapping = array_merge($config->get(), array('types' => array('foaf:Document', 'sioc:Item')));
+  $config->setData($bundle_mapping)->save();
+
+  // Set fields mapping config.
+  $node_shared_field_mappings = array(
+    'title' => array(
+      'properties' => array('dc:title'),
+    ),
+    'created' => array(
+      'properties' => array('dc:date', 'dc:created'),
+      'datatype' => 'xsd:dateTime',
+      'datatype_callback' => 'date_iso8601',
+    ),
+    'changed' => array(
+      'properties' => array('dc:modified'),
+      'datatype' => 'xsd:dateTime',
+      'datatype_callback' => 'date_iso8601',
+    ),
+    'body' => array(
+      'properties' => array('content:encoded'),
+    ),
+    'uid' => array(
+      'properties' => array('sioc:has_creator'),
+      'mapping_type' => 'rel',
+    ),
+    'name' => array(
+      'properties' => array('foaf:name'),
+    ),
+    'comment_count' => array(
+      'properties' => array('sioc:num_replies'),
+      'datatype' => 'xsd:integer',
+    ),
+    'last_activity' => array(
+      'properties' => array('sioc:last_activity_date'),
+      'datatype' => 'xsd:dateTime',
+      'datatype_callback' => 'date_iso8601',
     ),
   );
+  // Iterate over field mappings and save.
+  foreach ($node_shared_field_mappings as $field_name => $field_mapping) {
+    $config = $rdf_mapping_manager->getFieldMappingConfig('node', 'article', $field_name);
+    $field_mapping = array_merge($config->get(), $field_mapping);
+    $config->setData($field_mapping)->save();
+  }
 
-  foreach ($rdf_mappings as $rdf_mapping) {
-    rdf_mapping_save($rdf_mapping);
+  // Save comment RDF mapping configuration for article.
+  $rdf_mapping_manager = drupal_container()->get('rdf.mapping_manager');
+  // Save bundle mapping config.
+  $config = $rdf_mapping_manager->getBundleMappingConfig('comment', 'comment_node_article');
+  $bundle_mapping = array_merge($config->get(), array('types' => array('sioc:Post', 'sioct:Comment')));
+  $config->setData($bundle_mapping)->save();
+  $comment_shared_field_mappings = array(
+    'title' => array(
+      'properties' => array('dc:title'),
+    ),
+    'created' => array(
+      'properties' => array('dc:date', 'dc:created'),
+      'datatype' => 'xsd:dateTime',
+      'datatype_callback' => 'date_iso8601',
+    ),
+    'changed' => array(
+      'properties' => array('dc:modified'),
+      'datatype' => 'xsd:dateTime',
+      'datatype_callback' => 'date_iso8601',
+    ),
+    'comment_body' => array(
+      'properties' => array('content:encoded'),
+    ),
+    'pid' => array(
+      'properties' => array('sioc:reply_of'),
+      'mapping_type' => 'rel',
+    ),
+    'uid' => array(
+      'properties' => array('sioc:has_creator'),
+      'mapping_type' => 'rel',
+    ),
+    'name' => array(
+      'properties' => array('foaf:name'),
+    ),
+  );
+  // Iterate over shared field mappings and save.
+  foreach ($comment_shared_field_mappings as $field_name => $field_mapping) {
+    $config = $rdf_mapping_manager->getFieldMappingConfig('comment', 'comment_node_article', $field_name);
+    $field_mapping = array_merge($config->get(), $field_mapping);
+    $config->setData($field_mapping)->save();
   }
+
+  // Configure RDF mapping for User bundle.
+  $config = $rdf_mapping_manager->getBundleMappingConfig('user', 'user');
+  $bundle_mapping = array_merge($config->get(), array('types' => array('sioc:UserAccount')));
+  $config->setData($bundle_mapping)->save();
+  // @todo Change to reflect field names once User uses real fields.
+  // Configure RDF mapping for name field on User bundle.
+  $config = $rdf_mapping_manager->getFieldMappingConfig('user', 'user', 'name');
+  $field_mapping = array_merge($config->get(), array('properties' => array('foaf:name')));
+  $config->setData($field_mapping)->save();
+  // Configure RDF mapping for homepage field on User bundle.
+  $config = $rdf_mapping_manager->getFieldMappingConfig('user', 'user', 'homepage');
+  $field_mapping = array_merge($config->get(), array('properties' => array('foaf:page'), 'mapping_type' => 'rel'));
+  $config->setData($field_mapping)->save();
+
 }
diff --git a/core/modules/rdf/tests/rdf_test.module b/core/modules/rdf/tests/rdf_test.module
index 4d90472..b3d9bbc 100644
--- a/core/modules/rdf/tests/rdf_test.module
+++ b/core/modules/rdf/tests/rdf_test.module
@@ -1,59 +1 @@
 <?php
-
-/**
- * @file
- * Tests API interaction with the RDF module.
- */
-
-/**
- * Implements hook_rdf_mapping().
- */
-function rdf_test_rdf_mapping() {
-  return array(
-    array(
-      'type' => 'test_entity',
-      'bundle' => 'test_bundle',
-      'mapping' => array(
-        'rdftype' => array('sioc:Post'),
-        'title' => array(
-          'predicates' => array('dc:title'),
-        ),
-        'created' => array(
-          'predicates' => array('dc:created'),
-          'datatype' => 'xsd:dateTime',
-          'callback' => 'date_iso8601',
-        ),
-        'uid' => array(
-          'predicates' => array('sioc:has_creator', 'dc:creator'),
-          'type' => 'rel',
-        ),
-        'foobar' => array(
-          'predicates' => array('foo:bar'),
-        ),
-        'foobar1' => array(
-          'datatype' => 'foo:bar1type',
-          'predicates' => array('foo:bar1'),
-        ),
-        'foobar_objproperty1' => array(
-          'predicates' => array('sioc:has_creator', 'dc:creator'),
-          'type' => 'rel',
-        ),
-        'foobar_objproperty2' => array(
-          'predicates' => array('sioc:reply_of'),
-          'type' => 'rev',
-        ),
-      ),
-    ),
-  );
-}
-
-/**
- * Implements hook_rdf_namespaces().
- */
-function rdf_test_rdf_namespaces() {
-  return array(
-    'dc'       => 'http://purl.org/conflicting/namespace',
-    'foaf'     => 'http://xmlns.com/foaf/0.1/',
-    'foaf1'    => 'http://xmlns.com/foaf/0.1/',
-  );
-}
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
deleted file mode 100644
index 44e9460..0000000
--- a/core/modules/rdf/tests/rdf_test_mapping/lib/Drupal/rdf_test_mapping/EventSubscriber/TestMappingSubscriber.php
+++ /dev/null
@@ -1,58 +0,0 @@
-<?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
-   *   The mapping event.
-   */
-  public function mapTypesFromInput($event) {
-    $input_uris = $event->getInputUris();
-    $site_schema_types = $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);
-    $bundle_schema = $schema->bundle('entity_test', 'entity_test');
-    $site_schema_type = $bundle_schema->getUri();
-    $mapping = array(
-      self::STAGING_SITE_TYPE_URI => $site_schema_type,
-    );
-
-    foreach ($input_uris as $input_uri) {
-      // 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[$input_uri]) && isset($site_schema_types[$mapping[$input_uri]])) {
-        $event->setSiteSchemaUri($mapping[$input_uri]);
-      }
-    }
-  }
-
-  /**
-   * 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
deleted file mode 100644
index 3de3980..0000000
--- a/core/modules/rdf/tests/rdf_test_mapping/lib/Drupal/rdf_test_mapping/RdfTestMappingBundle.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?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
deleted file mode 100644
index d04be41..0000000
--- a/core/modules/rdf/tests/rdf_test_mapping/rdf_test_mapping.info
+++ /dev/null
@@ -1,6 +0,0 @@
-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
deleted file mode 100644
index b3d9bbc..0000000
--- a/core/modules/rdf/tests/rdf_test_mapping/rdf_test_mapping.module
+++ /dev/null
@@ -1 +0,0 @@
-<?php
diff --git a/core/modules/rdf/tests/rdf_test_mapping_event/lib/Drupal/rdf_test_mapping_event/EventSubscriber/TestMappingSubscriber.php b/core/modules/rdf/tests/rdf_test_mapping_event/lib/Drupal/rdf_test_mapping_event/EventSubscriber/TestMappingSubscriber.php
new file mode 100644
index 0000000..0a6aaf6
--- /dev/null
+++ b/core/modules/rdf/tests/rdf_test_mapping_event/lib/Drupal/rdf_test_mapping_event/EventSubscriber/TestMappingSubscriber.php
@@ -0,0 +1,95 @@
+<?php
+
+/**
+ * @file
+ * Contains TestMappingSubscriber.
+ */
+
+namespace Drupal\rdf_test_mapping_event\EventSubscriber;
+
+use Drupal\rdf\Mapping\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';
+  const STAGING_SITE_PROPERTY_URI = 'http://staging.com/entity_test_bundle/user_id';
+
+  /**
+   * Demonstrate mapping external type to site schema type on consumption.
+   *
+   * @param \Drupal\rdf\Mapping\MapTypesFromInputEvent $event
+   *   The mapping event.
+   */
+  public function mapTypesFromInput($event) {
+    $input_uris = $event->getInputUris();
+    $site_schema_types = $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);
+    $bundle_schema = $schema->bundle('entity_test', 'entity_test');
+    $site_schema_type = $bundle_schema->getUri();
+    $mapping = array(
+      self::STAGING_SITE_TYPE_URI => $site_schema_type,
+    );
+
+    foreach ($input_uris as $input_uri) {
+      // 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[$input_uri]) && isset($site_schema_types[$mapping[$input_uri]])) {
+        $event->setSiteSchemaUri($mapping[$input_uri]);
+      }
+    }
+  }
+
+  /**
+   * Demonstrate mapping bundle to external type for publishing.
+   *
+   * Generally, one should set mappings for output in the RDF mapping config
+   * instead of this event.
+   *
+   * @param \Drupal\rdf\Mapping\MapBundleForOutputEvent $event
+   *   The mapping event.
+   */
+  public function mapBundleForOutput($event) {
+    $term_schema = $event->getTermSchema();
+    $ids = $term_schema->getTermIds();
+    if ($ids['entity_type'] == 'entity_test' && $ids['bundle'] == 'entity_test') {
+      $event->addTypes(array(self::STAGING_SITE_TYPE_URI));
+    }
+  }
+
+  /**
+   * Demonstrate mapping field to external property for publishing.
+   *
+   * Generally, one should set mappings for output in the RDF mapping config
+   * instead of this event.
+   *
+   * @param \Drupal\rdf\Mapping\MapFieldForOutputEvent $event
+   *   The mapping event.
+   */
+  public function mapFieldForOutput($event) {
+    $term_schema = $event->getTermSchema();
+    $ids = $term_schema->getTermIds();
+    if ($ids['entity_type'] == 'entity_test' && $ids['bundle'] == 'entity_test' && $ids['field_name'] == 'user_id') {
+      $event->addPredicates(array(self::STAGING_SITE_PROPERTY_URI));
+    }
+  }
+
+  /**
+   * Implements EventSubscriberInterface::getSubscribedEvents().
+   */
+  static function getSubscribedEvents() {
+    $events[RdfMappingEvents::MAP_TYPES_FROM_INPUT] = 'mapTypesFromInput';
+    $events[RdfMappingEvents::MAP_BUNDLE_FOR_OUTPUT] = 'mapBundleForOutput';
+    $events[RdfMappingEvents::MAP_FIELD_FOR_OUTPUT] = 'mapFieldForOutput';
+    return $events;
+  }
+
+}
diff --git a/core/modules/rdf/tests/rdf_test_mapping_event/lib/Drupal/rdf_test_mapping_event/RdfTestMappingEventBundle.php b/core/modules/rdf/tests/rdf_test_mapping_event/lib/Drupal/rdf_test_mapping_event/RdfTestMappingEventBundle.php
new file mode 100644
index 0000000..0bd0c4e
--- /dev/null
+++ b/core/modules/rdf/tests/rdf_test_mapping_event/lib/Drupal/rdf_test_mapping_event/RdfTestMappingEventBundle.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file
+ * Contains RdfTestMappingBundle.
+ */
+
+namespace Drupal\rdf_test_mapping_event;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+/**
+ * RDF dependency injection container.
+ */
+class RdfTestMappingEventBundle extends Bundle {
+
+  /**
+   * Overrides Symfony\Component\HttpKernel\Bundle\Bundle::build().
+   */
+  public function build(ContainerBuilder $container) {
+    // Mapping subscriber.
+    $container->register('rdf_test_mapping_event.mapping', 'Drupal\rdf_test_mapping_event\EventSubscriber\TestMappingSubscriber')
+      ->addTag('event_subscriber');
+  }
+
+}
diff --git a/core/modules/rdf/tests/rdf_test_mapping_event/rdf_test_mapping_event.info b/core/modules/rdf/tests/rdf_test_mapping_event/rdf_test_mapping_event.info
new file mode 100644
index 0000000..3b34e19
--- /dev/null
+++ b/core/modules/rdf/tests/rdf_test_mapping_event/rdf_test_mapping_event.info
@@ -0,0 +1,6 @@
+name = "RDF module mapping event 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_event/rdf_test_mapping_event.module b/core/modules/rdf/tests/rdf_test_mapping_event/rdf_test_mapping_event.module
new file mode 100644
index 0000000..b3d9bbc
--- /dev/null
+++ b/core/modules/rdf/tests/rdf_test_mapping_event/rdf_test_mapping_event.module
@@ -0,0 +1 @@
+<?php
diff --git a/core/modules/rdf/tests/rdf_test_namespaces/rdf_test_namespaces.info b/core/modules/rdf/tests/rdf_test_namespaces/rdf_test_namespaces.info
new file mode 100644
index 0000000..c44f047
--- /dev/null
+++ b/core/modules/rdf/tests/rdf_test_namespaces/rdf_test_namespaces.info
@@ -0,0 +1,6 @@
+name = "RDF module namespaces test"
+description = "Test namespace declaration."
+package = Testing
+core = 8.x
+hidden = TRUE
+dependencies[] = rdf
diff --git a/core/modules/rdf/tests/rdf_test_namespaces/rdf_test_namespaces.module b/core/modules/rdf/tests/rdf_test_namespaces/rdf_test_namespaces.module
new file mode 100644
index 0000000..c938927
--- /dev/null
+++ b/core/modules/rdf/tests/rdf_test_namespaces/rdf_test_namespaces.module
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * @file
+ * Test the namespace registration functionality.
+ */
+
+/**
+ * Implements hook_rdf_namespaces().
+ */
+function rdf_test_namespaces_rdf_namespaces() {
+  return array(
+    'dc'       => 'http://purl.org/conflicting/namespace',
+    'foaf'     => 'http://xmlns.com/foaf/0.1/',
+    'foaf1'    => 'http://xmlns.com/foaf/0.1/',
+  );
+}
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index 4b1a2b1..c13a263 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -1293,51 +1293,6 @@ function taxonomy_field_settings_form($field, $instance, $has_data) {
 }
 
 /**
- * Implements hook_rdf_mapping().
- *
- * @return array
- *   The rdf mapping for vocabularies and terms.
- */
-function taxonomy_rdf_mapping() {
-  return array(
-    array(
-      'type' => 'taxonomy_term',
-      'bundle' => RDF_DEFAULT_BUNDLE,
-      'mapping' => array(
-        'rdftype' => array('skos:Concept'),
-        'name'   => array(
-          'predicates' => array('rdfs:label', 'skos:prefLabel'),
-        ),
-        'description'   => array(
-          'predicates' => array('skos:definition'),
-        ),
-        'vid'   => array(
-          'predicates' => array('skos:inScheme'),
-          'type' => 'rel',
-        ),
-        'parent'   => array(
-          'predicates' => array('skos:broader'),
-          'type' => 'rel',
-        ),
-      ),
-    ),
-    array(
-      'type' => 'taxonomy_vocabulary',
-      'bundle' => RDF_DEFAULT_BUNDLE,
-      'mapping' => array(
-        'rdftype' => array('skos:ConceptScheme'),
-        'name'   => array(
-          'predicates' => array('dc:title'),
-        ),
-        'description'   => array(
-          'predicates' => array('rdfs:comment'),
-        ),
-      ),
-    ),
-  );
-}
-
-/**
  * @defgroup taxonomy_index Taxonomy indexing
  * @{
  * Functions to maintain taxonomy indexing.
diff --git a/core/modules/tracker/tracker.pages.inc b/core/modules/tracker/tracker.pages.inc
index 853f50d..8695321 100644
--- a/core/modules/tracker/tracker.pages.inc
+++ b/core/modules/tracker/tracker.pages.inc
@@ -79,34 +79,37 @@ function tracker_page($account = NULL, $set_title = FALSE) {
       );
 
       // Adds extra RDFa markup to the $row array if the RDF module is enabled.
-      if (function_exists('rdf_mapping_load')) {
-        // Each node is not loaded for performance reasons, as a result we need
-        // to retrieve the RDF mapping for each node type.
-        $mapping = rdf_mapping_load('node', $node->type);
+      if (module_exists('rdf')) {
+        $mapping_manager = drupal_container()->get('rdf.mapping_manager');
         // Adds RDFa markup to the title of the node. Because the RDFa markup is
         // added to the td tag which might contain HTML code, we specify an
         // empty datatype to ensure the value of the title read by the RDFa
         // parsers is a plain literal.
-        $row['title'] += rdf_rdfa_attributes($mapping['title']) + array('datatype' => '');
+        $title_mapping = $mapping_manager->getFieldMapping('node', $node->type, 'title');
+        $row['title'] += rdf_rdfa_attributes($title_mapping) + array('datatype' => '');
         // Annotates the td tag containing the author of the node.
-        $row['author'] += rdf_rdfa_attributes($mapping['uid']);
+        $uid_mapping = $mapping_manager->getFieldMapping('node', $node->type, 'uid');
+        $row['author'] += rdf_rdfa_attributes($uid_mapping);
         // Annotates the td tag containing the number of replies. We add the
         // content attribute to ensure that only the comment count is used as
         // the value for 'num_replies'. Otherwise, other text such as a link
         // to the number of new comments could be included in the 'num_replies'
         // value.
-        $row['replies'] += rdf_rdfa_attributes($mapping['comment_count']);
+        $comment_count_mapping = $mapping_manager->getFieldMapping('node', $node->type, 'comment_count');
+        $row['replies'] += rdf_rdfa_attributes($comment_count_mapping);
         $row['replies'] += array('content' => $node->comment_count);
         // If the node has no comments, we assume the node itself was modified
         // and apply 'changed' in addition to 'last_activity'.  If there are
         // comments present, we cannot infer whether the node itself was
         // modified or a comment was posted, so we use only 'last_activity'.
-        $mapping_last_activity = rdf_rdfa_attributes($mapping['last_activity'], $node->last_activity);
+        $last_activity_mapping = $mapping_manager->getFieldMapping('node', $node->type, 'last_activity');
+        $last_activity_attributes = rdf_rdfa_attributes($last_activity_mapping, $node->last_activity);
         if ($node->comment_count == 0) {
-          $mapping_changed = rdf_rdfa_attributes($mapping['changed'], $node->last_activity);
-          $mapping_last_activity['property'] = array_merge($mapping_last_activity['property'], $mapping_changed['property']);
+          $changed_mapping = $mapping_manager->getFieldMapping('node', $node->type, 'changed');
+          $changed_attributes = rdf_rdfa_attributes($changed_mapping, $node->last_activity);
+          $last_activity_attributes['property'] = array_merge($last_activity_attributes['property'], $changed_attributes['property']);
         }
-        $row['last updated'] += $mapping_last_activity;
+        $row['last updated'] += $last_activity_attributes;
 
         // We need to add the about attribute on the tr tag to specify which
         // node the RDFa annotations above apply to. We move the content of
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 5570313..9b02f83 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -2662,28 +2662,6 @@ function user_cookie_delete($cookie_name) {
 }
 
 /**
- * Implements hook_rdf_mapping().
- */
-function user_rdf_mapping() {
-  return array(
-    array(
-      'type' => 'user',
-      'bundle' => RDF_DEFAULT_BUNDLE,
-      'mapping' => array(
-        'rdftype' => array('sioc:UserAccount'),
-        'name' => array(
-          'predicates' => array('foaf:name'),
-        ),
-        'homepage' => array(
-          'predicates' => array('foaf:page'),
-          'type' => 'rel',
-        ),
-      ),
-    ),
-  );
-}
-
-/**
  * Implements hook_file_download_access().
  */
 function user_file_download_access($field, EntityInterface $entity, File $file) {
diff --git a/core/profiles/standard/standard.install b/core/profiles/standard/standard.install
index 85c818c..97dd542 100644
--- a/core/profiles/standard/standard.install
+++ b/core/profiles/standard/standard.install
@@ -52,34 +52,6 @@ function standard_install() {
     node_add_body_field($type);
   }
 
-  // Insert default pre-defined RDF mapping into the database.
-  $rdf_mappings = array(
-    array(
-      'type' => 'node',
-      'bundle' => 'page',
-      'mapping' => array(
-        'rdftype' => array('foaf:Document'),
-      ),
-    ),
-    array(
-      'type' => 'node',
-      'bundle' => 'article',
-      'mapping' => array(
-        'field_image' => array(
-          'predicates' => array('og:image', 'rdfs:seeAlso'),
-          'type' => 'rel',
-        ),
-        'field_tags' => array(
-          'predicates' => array('dc:subject'),
-          'type' => 'rel',
-        ),
-      ),
-    ),
-  );
-  foreach ($rdf_mappings as $rdf_mapping) {
-    rdf_mapping_save($rdf_mapping);
-  }
-
   // Default "Basic page" to not be promoted and have comments disabled.
   variable_set('node_options_page', array('status'));
   variable_set('comment_page', COMMENT_NODE_HIDDEN);
@@ -272,4 +244,137 @@ function standard_install() {
   theme_enable(array('seven'));
   config('system.theme')->set('admin', 'seven')->save();
   variable_set('node_admin_theme', '1');
+
+  // Save the Node RDF mapping configuration.
+  $rdf_mapping_manager = drupal_container()->get('rdf.mapping_manager');
+  $node_shared_field_mappings = array(
+    'title' => array(
+      'properties' => array('dc:title'),
+    ),
+    'created' => array(
+      'properties' => array('dc:date', 'dc:created'),
+      'datatype' => 'xsd:dateTime',
+      'datatype_callback' => 'date_iso8601',
+    ),
+    'changed' => array(
+      'properties' => array('dc:modified'),
+      'datatype' => 'xsd:dateTime',
+      'datatype_callback' => 'date_iso8601',
+    ),
+    'body' => array(
+      'properties' => array('content:encoded'),
+    ),
+    'uid' => array(
+      'properties' => array('sioc:has_creator'),
+      'mapping_type' => 'rel',
+    ),
+    'name' => array(
+      'properties' => array('foaf:name'),
+    ),
+    'comment_count' => array(
+      'properties' => array('sioc:num_replies'),
+      'datatype' => 'xsd:integer',
+    ),
+    'last_activity' => array(
+      'properties' => array('sioc:last_activity_date'),
+      'datatype' => 'xsd:dateTime',
+      'datatype_callback' => 'date_iso8601',
+    ),
+  );
+  // Save the bundle mapping and shared field mappings for both node bundles.
+  foreach (array('article', 'page') as $bundle) {
+    // Save bundle mapping config.
+    $config = $rdf_mapping_manager->getBundleMappingConfig('node', $bundle);
+    $bundle_mapping = array_merge($config->get(), array('types' => array('foaf:Document', 'sioc:Item')));
+    $config->setData($bundle_mapping)->save();
+    // Iterate over shared field mappings and save.
+    foreach ($node_shared_field_mappings as $field_name => $field_mapping) {
+      $config = $rdf_mapping_manager->getFieldMappingConfig('node', $bundle, $field_name);
+      $field_mapping = array_merge($config->get(), $field_mapping);
+      $config->setData($field_mapping)->save();
+    }
+  }
+
+  // Save the RDF mappings for fields which are unique to articles.
+  $field_image_mapping = array(
+    'properties' => array('og:image', 'rdfs:seeAlso'),
+    'mapping_type' => 'rel',
+  );
+  $field_tags_mapping = array(
+    'properties' => array('dc:subject'),
+    'mapping_type' => 'rel',
+  );
+  // Configure RDF mapping for field_image on Article bundle.
+  $config = $rdf_mapping_manager->getFieldMappingConfig('node', 'article', 'field_image');
+  $field_mapping = array_merge($config->get(), $field_image_mapping);
+  $config->setData($field_mapping)->save();
+  // Configure RDF mapping for field_tags on Article bundle.
+  $config = $rdf_mapping_manager->getFieldMappingConfig('node', 'article', 'field_tags');
+  $field_mapping = array_merge($config->get(), $field_tags_mapping);
+  $config->setData($field_mapping)->save();
+
+  // Save comment RDF mapping configuration for article.
+  $rdf_mapping_manager = drupal_container()->get('rdf.mapping_manager');
+  // Save bundle mapping config.
+  $config = $rdf_mapping_manager->getBundleMappingConfig('comment', 'comment_node_article');
+  $bundle_mapping = array_merge($config->get(), array('types' => array('sioc:Post', 'sioct:Comment')));
+  $config->setData($bundle_mapping)->save();
+  $comment_shared_field_mappings = array(
+    'title' => array(
+      'properties' => array('dc:title'),
+    ),
+    'created' => array(
+      'properties' => array('dc:date', 'dc:created'),
+      'datatype' => 'xsd:dateTime',
+      'datatype_callback' => 'date_iso8601',
+    ),
+    'changed' => array(
+      'properties' => array('dc:modified'),
+      'datatype' => 'xsd:dateTime',
+      'datatype_callback' => 'date_iso8601',
+    ),
+    'comment_body' => array(
+      'properties' => array('content:encoded'),
+    ),
+    'pid' => array(
+      'properties' => array('sioc:reply_of'),
+      'mapping_type' => 'rel',
+    ),
+    'uid' => array(
+      'properties' => array('sioc:has_creator'),
+      'mapping_type' => 'rel',
+    ),
+    'name' => array(
+      'properties' => array('foaf:name'),
+    ),
+  );
+  // Iterate over shared field mappings and save.
+  foreach ($comment_shared_field_mappings as $field_name => $field_mapping) {
+    $config = $rdf_mapping_manager->getFieldMappingConfig('comment', 'comment_node_article', $field_name);
+    $field_mapping = array_merge($config->get(), $field_mapping);
+    $config->setData($field_mapping)->save();
+  }
+
+  // Configure RDF mapping for User bundle.
+  $config = $rdf_mapping_manager->getBundleMappingConfig('user', 'user');
+  $bundle_mapping = array_merge($config->get(), array('types' => array('sioc:UserAccount')));
+  $config->setData($bundle_mapping)->save();
+  // @todo Change to reflect field names once User uses real fields.
+  // Configure RDF mapping for name field on User bundle.
+  $config = $rdf_mapping_manager->getFieldMappingConfig('user', 'user', 'name');
+  $field_mapping = array_merge($config->get(), array('properties' => array('foaf:name')));
+  $config->setData($field_mapping)->save();
+  // Configure RDF mapping for homepage field on User bundle.
+  $config = $rdf_mapping_manager->getFieldMappingConfig('user', 'user', 'homepage');
+  $field_mapping = array_merge($config->get(), array('properties' => array('foaf:page'), 'mapping_type' => 'rel'));
+  $config->setData($field_mapping)->save();
+
+  // Configure RDF mapping for Tags bundle.
+  $config = $rdf_mapping_manager->getBundleMappingConfig('taxonomy_term', 'tags');
+  $bundle_mapping = array_merge($config->get(), array('types' => array('skos:Concept')));
+  $config->setData($bundle_mapping)->save();
+  // Configure RDF mapping for name field on Tags bundle.
+  $config = $rdf_mapping_manager->getFieldMappingConfig('taxonomy_term', 'tags', 'name');
+  $field_mapping = array_merge($config->get(), array('properties' => array('rdfs:label', 'skos:prefLabel')));
+  $config->setData($field_mapping)->save();
 }
