diff --git a/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityNormalizer.php b/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityNormalizer.php
index b286539..b3dbffa 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 b64a1dc..0253270 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -842,53 +842,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/config/rdf.namespace.rdf_content.yml b/core/modules/rdf/config/rdf.namespace.rdf_content.yml
new file mode 100644
index 0000000..fe61f81
--- /dev/null
+++ b/core/modules/rdf/config/rdf.namespace.rdf_content.yml
@@ -0,0 +1,5 @@
+nsid: rdf_content
+namespace_uri: 'http://purl.org/rss/1.0/modules/content/'
+prefix: content
+module: rdf
+langcode: und
diff --git a/core/modules/rdf/config/rdf.namespace.rdf_dc.yml b/core/modules/rdf/config/rdf.namespace.rdf_dc.yml
new file mode 100644
index 0000000..a9ef6d0
--- /dev/null
+++ b/core/modules/rdf/config/rdf.namespace.rdf_dc.yml
@@ -0,0 +1,5 @@
+nsid: rdf_dc
+namespace_uri: 'http://purl.org/dc/terms/'
+prefix: dc
+module: rdf
+langcode: und
diff --git a/core/modules/rdf/config/rdf.namespace.rdf_foaf.yml b/core/modules/rdf/config/rdf.namespace.rdf_foaf.yml
new file mode 100644
index 0000000..36dc2ae
--- /dev/null
+++ b/core/modules/rdf/config/rdf.namespace.rdf_foaf.yml
@@ -0,0 +1,5 @@
+nsid: rdf_foaf
+namespace_uri: 'http://xmlns.com/foaf/0.1/'
+prefix: foaf
+module: rdf
+langcode: und
diff --git a/core/modules/rdf/config/rdf.namespace.rdf_og.yml b/core/modules/rdf/config/rdf.namespace.rdf_og.yml
new file mode 100644
index 0000000..dd58c1a
--- /dev/null
+++ b/core/modules/rdf/config/rdf.namespace.rdf_og.yml
@@ -0,0 +1,5 @@
+nsid: rdf_og
+namespace_uri: 'http://ogp.me/ns#'
+prefix: og
+module: rdf
+langcode: und
diff --git a/core/modules/rdf/config/rdf.namespace.rdf_rdfs.yml b/core/modules/rdf/config/rdf.namespace.rdf_rdfs.yml
new file mode 100644
index 0000000..ae83291
--- /dev/null
+++ b/core/modules/rdf/config/rdf.namespace.rdf_rdfs.yml
@@ -0,0 +1,5 @@
+nsid: rdf_rdfs
+namespace_uri: 'http://www.w3.org/2000/01/rdf-schema#'
+prefix: rdfs
+module: rdf
+langcode: und
diff --git a/core/modules/rdf/config/rdf.namespace.rdf_schema.yml b/core/modules/rdf/config/rdf.namespace.rdf_schema.yml
new file mode 100644
index 0000000..d9dae3d
--- /dev/null
+++ b/core/modules/rdf/config/rdf.namespace.rdf_schema.yml
@@ -0,0 +1,5 @@
+nsid: rdf_schema
+namespace_uri: 'http://schema.org/'
+prefix: schema
+module: rdf
+langcode: und
diff --git a/core/modules/rdf/config/rdf.namespace.rdf_sioc.yml b/core/modules/rdf/config/rdf.namespace.rdf_sioc.yml
new file mode 100644
index 0000000..69d9402
--- /dev/null
+++ b/core/modules/rdf/config/rdf.namespace.rdf_sioc.yml
@@ -0,0 +1,5 @@
+nsid: rdf_sioc
+namespace_uri: 'http://rdfs.org/sioc/ns#'
+prefix: sioc
+module: rdf
+langcode: und
diff --git a/core/modules/rdf/config/rdf.namespace.rdf_sioct.yml b/core/modules/rdf/config/rdf.namespace.rdf_sioct.yml
new file mode 100644
index 0000000..df36951
--- /dev/null
+++ b/core/modules/rdf/config/rdf.namespace.rdf_sioct.yml
@@ -0,0 +1,5 @@
+nsid: rdf_sioct
+namespace_uri: 'http://rdfs.org/sioc/types#'
+prefix: sioct
+module: rdf
+langcode: und
diff --git a/core/modules/rdf/config/rdf.namespace.rdf_skos.yml b/core/modules/rdf/config/rdf.namespace.rdf_skos.yml
new file mode 100644
index 0000000..54b4591
--- /dev/null
+++ b/core/modules/rdf/config/rdf.namespace.rdf_skos.yml
@@ -0,0 +1,5 @@
+nsid: rdf_skos
+namespace_uri: 'http://www.w3.org/2004/02/skos/core#'
+prefix: skos
+module: rdf
+langcode: und
diff --git a/core/modules/rdf/config/rdf.namespace.rdf_xsd.yml b/core/modules/rdf/config/rdf.namespace.rdf_xsd.yml
new file mode 100644
index 0000000..c4524f3
--- /dev/null
+++ b/core/modules/rdf/config/rdf.namespace.rdf_xsd.yml
@@ -0,0 +1,5 @@
+nsid: rdf_xsd
+namespace_uri: 'http://www.w3.org/2001/XMLSchema#'
+prefix: xsd
+module: rdf
+langcode: und
diff --git a/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/MappingSubscriber.php b/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/MappingSubscriber.php
index 764c2f9..cc0104e 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,50 @@ 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');
+
+      if (isset($datatype)) {
+        $event->setDatatype($datatype);
+      }
+      if (isset($datatype_callback)) {
+        $event->setDatatypeCallback($datatype_callback);
+      }
+    }
+  }
+
   /**
    * 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/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..682c67f
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Mapping/MapFieldForOutputEvent.php
@@ -0,0 +1,90 @@
+<?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;
+
+  /**
+   * 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;
+  }
+
+  public function getPredicates() {
+    return $this->predicates;
+  }
+
+  public function getDatatype() {
+    return $this->datatype;
+  }
+
+  public function getDatatypeCallback() {
+    return $this->datatypeCallback;
+  }
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/MapTypesFromInputEvent.php b/core/modules/rdf/lib/Drupal/rdf/Mapping/MapTypesFromInputEvent.php
similarity index 98%
rename from core/modules/rdf/lib/Drupal/rdf/MapTypesFromInputEvent.php
rename to core/modules/rdf/lib/Drupal/rdf/Mapping/MapTypesFromInputEvent.php
index c54fe0d..7ca6ea4 100644
--- a/core/modules/rdf/lib/Drupal/rdf/MapTypesFromInputEvent.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Mapping/MapTypesFromInputEvent.php
@@ -5,7 +5,7 @@
  * Contains MapInputTypesEvent.
  */
 
-namespace Drupal\rdf;
+namespace Drupal\rdf\Mapping;
 
 use Symfony\Component\EventDispatcher\Event;
 
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/RdfMappingException.php b/core/modules/rdf/lib/Drupal/rdf/Mapping/RdfMappingException.php
similarity index 84%
rename from core/modules/rdf/lib/Drupal/rdf/RdfMappingException.php
rename to core/modules/rdf/lib/Drupal/rdf/Mapping/RdfMappingException.php
index d96722f..8b4e116 100644
--- a/core/modules/rdf/lib/Drupal/rdf/RdfMappingException.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Mapping/RdfMappingException.php
@@ -5,7 +5,7 @@
  * Contains RdfMappingException.
  */
 
-namespace Drupal\rdf;
+namespace Drupal\rdf\Mapping;
 
 /**
  * Exception to use when no RDF mapping is found.
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..6407c99
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Mapping/RdfMappingManager.php
@@ -0,0 +1,253 @@
+<?php
+
+/**
+ * @file
+ * Contains RdfMappingManager.
+ */
+
+namespace Drupal\rdf\Mapping;
+
+use ReflectionClass;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\rdf\RdfNamespaceManager;
+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;
+
+  /**
+   * The RDF namespace manager.
+   *
+   * @var \Drupal\rdf\RdfNamespaceManager
+   */
+  protected $namespaceManager;
+
+  /**
+   * 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, RdfNamespaceManager $namespace_manager) {
+    $this->dispatcher = $dispatcher;
+    $this->siteSchemaManager = $site_schema_manager;
+    $this->namespaceManager = $namespace_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();
+    // Any part of the mapping which contains CURIEs will need to be prepared
+    // to handle the possibility of prefix conflicts.
+    if (!empty($types)) {
+      $mapping['types'] = $this->namespaceManager->prepareCuries($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'] = $this->namespaceManager->prepareCuries($predicates);
+      $mapping['datatype'] = $map_event->getDatatype();
+      $mapping['datatype_callback'] = $map_event->getDatatypeCallback();
+    }
+    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..bd3cbe1
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/BundleRdfMapping.php
@@ -0,0 +1,79 @@
+<?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.
+   *
+   * Each array value can be in one of two forms:
+   * - A CURIE string, e.g. schema:Article. If this form is used, the prefix's
+   *   namespace mapping must be defined by core's RDF module.
+   * - An array containing:
+   *   - curie: The CURIE string.
+   *   - prefix_module: The module in which defines the prefix's namespace
+   *     mapping.
+   *
+   * @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..e623006
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/FieldRdfMapping.php
@@ -0,0 +1,100 @@
+<?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.
+   *
+   * Each array value can be in one of two forms:
+   * - A CURIE string, e.g. schema:name. If this form is used, the prefix's
+   *   namespace mapping must be defined by core's RDF module.
+   * - An array containing:
+   *   - curie: The CURIE string.
+   *   - prefix_module: The module in which defines the prefix's namespace
+   *     mapping.
+   *
+   * @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;
+
+  /**
+   * Implements Drupal\Core\Entity\EntityInterface::id().
+   */
+  public function id() {
+    return $this->mid;
+  }
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/RdfNamespace.php b/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/RdfNamespace.php
new file mode 100644
index 0000000..7b460df
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/RdfNamespace.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\rdf\Plugin\Core\Entity\RdfNamespace.
+ */
+
+namespace Drupal\rdf\Plugin\Core\Entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Defines the RDF namespace entity.
+ *
+ * @Plugin(
+ *   id = "rdf_namespace",
+ *   label = @Translation("RDF namespace"),
+ *   module = "rdf",
+ *   controller_class = "Drupal\rdf\RdfNamespaceStorageController",
+ *   form_controller_class = {
+ *     "default" = "Drupal\Core\Entity\EntityFormController"
+ *   },
+ *   config_prefix = "rdf.namespace",
+ *   entity_keys = {
+ *     "id" = "nsid",
+ *     "label" = "prefix"
+ *   },
+ *   view_modes = {
+ *     "full" = {
+ *       "label" = @Translation("RDF Namespace"),
+ *       "custom_settings" = FALSE
+ *     }
+ *   }
+ * )
+ */
+class RdfNamespace extends ConfigEntityBase {
+
+  /**
+   * The RDF namespace ID.
+   *
+   * @var integer
+   */
+  public $nsid;
+
+  /**
+   * The full namespace URI for this namespace.
+   *
+   * @var string
+   */
+  public $namespace_uri;
+
+  /**
+   * The preferred prefix for this vocabulary.
+   *
+   * @var string
+   */
+  public $prefix;
+
+  /**
+   * The module which defines this namespace/prefix mapping.
+   *
+   * @var string
+   */
+  public $module;
+
+  /**
+   * Overrides \Drupal\Core\Entity\Entity::id().
+   */
+  public function id() {
+    return $this->nsid;
+  }
+
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/RdfBundle.php b/core/modules/rdf/lib/Drupal/rdf/RdfBundle.php
index 40affcc..b2c5b3b 100644
--- a/core/modules/rdf/lib/Drupal/rdf/RdfBundle.php
+++ b/core/modules/rdf/lib/Drupal/rdf/RdfBundle.php
@@ -29,10 +29,13 @@ public function build(ContainerBuilder $container) {
     // Site schema manager service.
     $container->register('rdf.site_schema_manager', 'Drupal\rdf\SiteSchema\SiteSchemaManager')
       ->addArgument(new Reference('cache.rdf.site_schema.types'));
+    // Namespace manager service.
+    $container->register('rdf.namespace_manager', 'Drupal\rdf\RdfNamespaceManager');
     // 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'));
+      ->addArgument(new Reference('rdf.site_schema_manager'))
+      ->addArgument(new Reference('rdf.namespace_manager'));
 
     // Mapping subscriber.
     $container->register('rdf.mapping', 'Drupal\rdf\EventSubscriber\MappingSubscriber')
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/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/RdfNamespaceManager.php b/core/modules/rdf/lib/Drupal/rdf/RdfNamespaceManager.php
new file mode 100644
index 0000000..ba491a7
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/RdfNamespaceManager.php
@@ -0,0 +1,121 @@
+<?php
+
+/**
+ * @file
+ * Contains RdfNamespaceManager.
+ */
+
+namespace Drupal\rdf;
+
+class RdfNamespaceManager {
+
+  /**
+   * Gets the array of namespace declarations.
+   *
+   * @return array
+   *   Array of namespace URIs keyed by prefix, e.g. schema => http://schema.org
+   */
+  public function getNamespaceDeclarations() {
+    $ns = array();
+    foreach ($this->getNamespaceDefinitions() as $definitions) {
+      foreach ($definitions as $namespace) {
+        $ns[$namespace->prefix] = $namespace->namespace_uri;
+      }
+    }
+    return $ns;
+  }
+
+  /**
+   * Gets a nested array of RdfNamespace config entities.
+   *
+   * Multiple modules may use the same prefix to correspond to different URIs.
+   * To disambiguate between multiple definitions, prefixes that conflict are
+   * appended with a number, e.g. schema1.
+   *
+   * @return array
+   *   A nested array of namespace objects, keyed first by prefix, then by
+   *   defining module.
+   */
+  public function getNamespaceDefinitions() {
+    $prefixes = array();
+
+    // Compile the definitions into a nested array, keyed by prefix and then
+    // defining module.
+    foreach (entity_load_multiple('rdf_namespace') as $namespace) {
+      $prefixes[$namespace->prefix][$namespace->module] = $namespace;
+    }
+
+    // If there is more than one definition for a prefix, then the additional
+    // definitions should use a different prefix.
+    foreach ($prefixes as $prefix => $definitions) {
+      if (count($definitions) > 1) {
+        $i = 1;
+        foreach ($definitions as $module => $namespace) {
+          if ($module == 'rdf') {
+            continue;
+          }
+          else {
+            $namespace->prefix = $prefix . $i;
+            $i++;
+          }
+        }
+      }
+    }
+
+    return $prefixes;
+  }
+
+  /**
+   * Converts a mixed array of CURIE strings and CURIE info arrays to strings.
+   *
+   * Prefixes in CURIEs may change if there is a conflict between two namespace
+   * definitions. To ensure that CURIEs use the correct prefix, they should be
+   * passed through this function. CURIEs that use prefixes which are not
+   * registered by core's RDF module should represent the CURIE as an array with
+   * the following format:
+   * - curie: The CURIE string, using the prefix registered in the definition.
+   * - prefix_module: The module which registered the definition.
+   *
+   * @param array $terms
+   *   A mixed array of CURIE strings and CURIE info arrays.
+   *
+   * @return array
+   *   An array of CURIE strings.
+   *
+   * @throws \UnexpectedValueException
+   */
+  public function prepareCuries($terms) {
+    $curies = array();
+    $definitions = $this->getNamespaceDefinitions();
+
+    foreach ($terms as $term) {
+      // If this CURIE is a string, it uses a namespace definition from RDF
+      // module, which are always used as is.
+      if (is_string($term)) {
+        $curies[] = $term;
+      }
+      // A term shouldn't be formatted as an array if it uses RDF module's
+      // definition, but handle it anyway.
+      else if ($term['prefix_module'] == 'rdf') {
+        $curies[] = $term['curie'];
+      }
+      // Handle terms which use namespace definitions from other modules.
+      else {
+        $prefix_parts = explode(':', $term['curie']);
+        if (count($prefix_parts) != 2) {
+          throw new \UnexpectedValueException(sprintf('RDF mapping uses malformed CURIE %s. CURIEs must be in prefix:local_name format.', $term['curie']));
+        }
+        $prefix = $prefix_parts[0];
+        $local_name = $prefix_parts[1];
+        $module = $term['prefix_module'];
+        $namespace = $definitions[$prefix][$module];
+        if (!isset($definitions[$prefix][$module])) {
+          throw new \UnexpectedValueException(sprintf('Prefix %s is not defined in module %s.', $prefix, $module));
+        }
+        $curies[] = $namespace->prefix . ':' . $local_name;
+      }
+    }
+
+    return $curies;
+  }
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/RdfNamespaceStorageController.php b/core/modules/rdf/lib/Drupal/rdf/RdfNamespaceStorageController.php
new file mode 100644
index 0000000..9a4d39b
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/RdfNamespaceStorageController.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\rdf\RdfNamespaceStorageController.
+ */
+
+namespace Drupal\rdf;
+
+use Drupal\Core\Config\Entity\ConfigStorageController;
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Storage controller for RDF namespaces.
+ */
+class RdfNamespaceStorageController extends ConfigStorageController {
+
+  /**
+   * Overrides \Drupal\Core\Config\Entity\ConfigStorageController::save().
+   */
+  public function save(EntityInterface $entity) {
+    // @todo Add exception for required properties.
+    $entity->nsid = $entity->module . '_' . $entity->prefix;
+    parent::save($entity);
+  }
+
+}
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/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..5def52a 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(
@@ -36,9 +36,15 @@ function testGetRdfNamespaces() {
     // Get all RDF namespaces.
     $ns = rdf_get_namespaces();
 
-    $this->assertEqual($ns['rdfs'], 'http://www.w3.org/2000/01/rdf-schema#', 'A prefix declared once is included.');
-    $this->assertEqual($ns['foaf'], 'http://xmlns.com/foaf/0.1/', 'The same prefix declared in several implementations of hook_rdf_namespaces() is valid as long as all the namespaces are the same.');
-    $this->assertEqual($ns['foaf1'], 'http://xmlns.com/foaf/0.1/', 'Two prefixes can be assigned the same namespace.');
-    $this->assertEqual($ns['dc'], 'http://purl.org/dc/terms/', 'When a prefix has conflicting namespaces, the first declared one is used.');
+    // Test mapping defined in RDF with no conflicts.
+    $this->assertEqual($ns['rdfs'], 'http://www.w3.org/2000/01/rdf-schema#', 'A prefix declared in RDF module is included.');
+
+    // Test mapping defined in RDF with conflict from test module.
+    $this->assertEqual($ns['schema'], 'http://schema.org/', 'Namespace mapping declared by RDF module takes precedence when registered twice.');
+    // Test conflicting mapping.
+    $this->assertEqual($ns['schema1'], 'http://test.org/', 'Namespace mapping from other module is appended with "1" when conflicting with RDF module.');
+
+    // Test mapping defined in test module with no conflicts.
+    $this->assertEqual($ns['test_prefix'], 'http://test-prefix.org/', 'Namespace mapping from other module is not appended with "1" when there is no conflicting prefix');
   }
 }
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/PrepareCuriesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/PrepareCuriesTest.php
new file mode 100644
index 0000000..9f8bb5f
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/PrepareCuriesTest.php
@@ -0,0 +1,104 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\rdf\Tests\PrepareCuriesTest.
+ */
+
+namespace Drupal\rdf\Tests;
+
+use Drupal\rdf\RdfNamespaceManager;
+use Drupal\simpletest\DrupalUnitTestBase;
+
+/**
+ * Tests that CURIEs from conflicting namespace mappings are properly handled.
+ */
+class PrepareCuriesTest extends DrupalUnitTestBase {
+
+  public static $modules = array('system', 'rdf', 'rdf_test_namespaces');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'CURIE handling',
+      'description' => 'Test that CURIEs from conflicting namespace mappings are properly handled..',
+      'group' => 'RDF',
+    );
+  }
+
+  /**
+   * Overrides \Drupal\simpletest\DrupalUnitTestBase::setUp().
+   */
+  public function setUp() {
+    parent::setUp();
+    $this->enableModules(array('rdf', 'rdf_test_namespaces'));
+    $this->namespaceManager = new RdfNamespaceManager();
+  }
+
+  /**
+   * Tests CURIE generation when a module's namespace conflicts with core's.
+   */
+  function testCoreConflict() {
+    $terms = array(
+      'core-conflict:Foo',
+      array(
+        'curie' => 'core-conflict:Bar',
+        'prefix_module' => 'core_conflict2',
+      ),
+    );
+
+    $expectedCuries = array(
+      'core-conflict:Foo',
+      'core-conflict1:Bar',
+    );
+
+    $curies = $this->namespaceManager->prepareCuries($terms);
+    $this->assertEqual($curies, $expectedCuries, 'CURIEs prepared as expected when contrib prefix conflicts with core.');
+  }
+
+  /**
+   * Tests CURIE generation when a module declares a non-conflicting namespace.
+   */
+  function testContribNoConflict() {
+    $terms = array(
+      array(
+        'curie' => 'no-conflict:Foo',
+        'prefix_module' => 'no_conflict',
+      ),
+    );
+
+    $expectedCuries = array(
+      'no-conflict:Foo',
+    );
+
+    $curies = $this->namespaceManager->prepareCuries($terms);
+    $this->assertEqual($curies, $expectedCuries, 'CURIE prepared as expected when contrib prefix has no conflicts.');
+  }
+
+  /**
+   * Tests CURIE generation when contrib modules declare conflicting namespaces.
+   */
+  function testContribConflict() {
+    $terms = array(
+      array(
+        'curie' => 'contrib-conflict:Foo',
+        'prefix_module' => 'contrib_conflict',
+      ),
+      array(
+        'curie' => 'contrib-conflict:Foo',
+        'prefix_module' => 'contrib_conflict2',
+      ),
+    );
+
+    $expectedCuries = array(
+      'contrib-conflict1:Foo',
+      'contrib-conflict2:Foo',
+    );
+
+    $curies = $this->namespaceManager->prepareCuries($terms);
+    // Since there is no preference given to either module, it is not certain
+    // which will take priority and be given '1' vs '2', so assertEqual can't
+    // be used. Instead, just ensure both elements are in the array.
+    $difference = array_diff($curies, $expectedCuries);
+    $this->assertTrue(empty($difference), 'CURIEs prepared as expected when twp contrib modules have prefix conflicts.');
+  }
+}
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..92362ee 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'), '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/rdf.install b/core/modules/rdf/rdf.install
deleted file mode 100644
index 10d3f8d..0000000
--- a/core/modules/rdf/rdf.install
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-
-/**
- * @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 8f41a85..834b519 100644
--- a/core/modules/rdf/rdf.module
+++ b/core/modules/rdf/rdf.module
@@ -77,39 +77,10 @@ function rdf_help($path, $arg) {
 const RDF_DEFAULT_BUNDLE = '';
 
 /**
- * Implements hook_rdf_namespaces().
- */
-function rdf_rdf_namespaces() {
-  return array(
-    'content'  => 'http://purl.org/rss/1.0/modules/content/',
-    'dc'       => 'http://purl.org/dc/terms/',
-    'foaf'     => 'http://xmlns.com/foaf/0.1/',
-    'og'       => 'http://ogp.me/ns#',
-    'rdfs'     => 'http://www.w3.org/2000/01/rdf-schema#',
-    'sioc'     => 'http://rdfs.org/sioc/ns#',
-    'sioct'    => 'http://rdfs.org/sioc/types#',
-    'skos'     => 'http://www.w3.org/2004/02/skos/core#',
-    'xsd'      => 'http://www.w3.org/2001/XMLSchema#',
-  );
-}
-
-/**
  * Retrieves RDF namespaces.
- *
- * Invokes hook_rdf_namespaces() and collects RDF namespaces from modules that
- * implement it.
  */
 function rdf_get_namespaces() {
-  $namespaces = array();
-  // In order to resolve duplicate namespaces by using the earliest defined
-  // namespace, do not use module_invoke_all().
-  foreach (module_implements('rdf_namespaces') as $module) {
-    $function = $module . '_rdf_namespaces';
-    if (function_exists($function)) {
-      $namespaces = NestedArray::mergeDeep($function(), $namespaces);
-    }
-  }
-  return $namespaces;
+  return drupal_container()->get('rdf.namespace_manager')->getNamespaceDeclarations();
 }
 
 /**
@@ -118,100 +89,34 @@ function rdf_get_namespaces() {
  * @param $type
  *   An entity type.
  * @param $bundle
- *   (optional) A bundle name.
+ *   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'];
-        }
+function rdf_mapping_load($type, $bundle) {
+  $mapping_manager = drupal_container()->get('rdf.mapping_manager');
+  $mapping = array(
+    'rdftypes' => $mapping_manager->getBundleMapping($type, $bundle),
+  );
+  $entity = entity_create($type, array('type' => $bundle));
+
+  $properties = $entity->getPropertyDefinitions();
+  if (!empty($properties)) {
+    foreach ($properties as $field_name => $field_info) {
+      $field_mapping = $mapping_manager->getFieldMapping($type, $bundle, $field_name);
+      if (!empty($field_mapping)) {
+        $mapping[$field_name] = $field_mapping;
       }
     }
   }
-
-  return isset($default_mappings[$type]) ? $default_mappings[$type] : array();
+  return $mapping;
 }
 
 /**
- * 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.
+ * @} End of "defgroup rdf".
  */
-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
@@ -219,61 +124,6 @@ function _rdf_mapping_load_multiple($type, array $bundles) {
  */
 
 /**
- * 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
@@ -309,13 +159,13 @@ function rdf_rdfa_attributes($mapping, $data = NULL) {
     // 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'];
@@ -335,96 +185,6 @@ 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().
- */
-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);
-    $comment->rdf_data['nid_uri'] = url('node/' . $comment->nid->value);
-    if ($comment->pid->value) {
-      $comment->rdf_data['pid_uri'] = url('comment/' . $comment->pid->value, array('fragment' => 'comment-' . $comment->pid->value));
-    }
-  }
-}
-
-/**
  * Implements hook_theme().
  */
 function rdf_theme() {
@@ -499,14 +259,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['rdftype']) ? NULL : $bundle_mapping['rdftype'];
 
   // 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 +282,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 +330,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 +344,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->getFieldMappingConfig($entity_type, $bundle, $field_name)->get();
 
-  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,37 +377,38 @@ 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
-  // sioc:UserAccount and the foaf:Person who holds the account.
-  if (current_path() == $uri['path']) {
-    $account_holder_meta = array(
-      '#tag' => 'meta',
-      '#attributes' => array(
-        'about' => url($uri['path'], array_merge($uri['options'], array('fragment' => 'me'))),
-        'typeof' => array('foaf:Person'),
-        'rel' => array('foaf:account'),
-        'resource' => url($uri['path'], $uri['options']),
-      ),
-    );
-    // Adds the markup for username.
-    $username_meta = array(
-      '#tag' => 'meta',
-      '#attributes' => array(
-        'about' => url($uri['path'], $uri['options']),
-        'property' => $account->rdf_mapping['name']['predicates'],
-        'content' => $account->name,
-      )
-    );
-    drupal_add_html_head($account_holder_meta, 'rdf_user_account_holder');
-    drupal_add_html_head($username_meta, 'rdf_user_username');
-  }
+  // Adds the relationship between the sioc:UserAccount and the foaf:Person who
+  // holds the account.
+  $account_holder_meta = array(
+    '#tag' => 'meta',
+    '#attributes' => array(
+      'about' => url($uri['path'], array_merge($uri['options'], array('fragment' => 'me'))),
+      'typeof' => array('foaf:Person'),
+      'rel' => array('foaf:account'),
+      'resource' => url($uri['path'], $uri['options']),
+    ),
+  );
+  // Adds the markup for username.
+  $name_mapping = $mapping_manager->getFieldMapping('user', 'user', 'name');
+  $username_meta = array(
+    '#tag' => 'meta',
+    '#attributes' => array(
+      'about' => url($uri['path'], $uri['options']),
+      'property' => $name_mapping['properties'],
+      'content' => $account->name,
+    )
+  );
+  drupal_add_html_head($account_holder_meta, 'rdf_user_account_holder');
+  drupal_add_html_head($username_meta, 'rdf_user_username');
 }
 
 /**
@@ -683,12 +453,12 @@ function rdf_preprocess_username(&$variables) {
   // 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'];
+    $attributes['property'] = $rdf_mapping['name']['properties'];
     $attributes['datatype'] = '';
   }
   // Add the homepage RDFa markup if present.
   if (!empty($variables['homepage']) && !empty($rdf_mapping['homepage'])) {
-    $attributes['rel'] = $rdf_mapping['homepage']['predicates'];
+    $attributes['rel'] = $rdf_mapping['homepage']['properties'];
   }
   // The remaining attributes can have multiple values listed, with whitespace
   // separating the values in the RDFa attributes
@@ -723,15 +493,15 @@ function rdf_preprocess_comment(&$variables) {
   }
   // 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'];
+    $variables['rdf_template_variable_attributes']['author']['rel'] = $comment->rdf_mapping['uid']['properties'];
+    $variables['rdf_template_variable_attributes']['submitted']['rel'] = $comment->rdf_mapping['uid']['properties'];
   }
   if (!empty($comment->rdf_mapping['title'])) {
     // 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'] = $comment->rdf_mapping['title']['properties'];
     $variables['title_attributes']['datatype'] = '';
   }
 
@@ -739,7 +509,7 @@ function rdf_preprocess_comment(&$variables) {
   // it belongs to. If available, the parent comment is also annotated.
   if (!empty($comment->rdf_mapping['pid'])) {
     // Adds the relation to the parent node.
-    $parent_node_attributes['rel'] = $comment->rdf_mapping['pid']['predicates'];
+    $parent_node_attributes['rel'] = $comment->rdf_mapping['pid']['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'];
@@ -760,14 +530,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(),
       ),
     );
@@ -788,13 +561,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.module b/core/modules/rdf/tests/rdf_test.module
index 4d90472..27e4382 100644
--- a/core/modules/rdf/tests/rdf_test.module
+++ b/core/modules/rdf/tests/rdf_test.module
@@ -46,14 +46,3 @@ function rdf_test_rdf_mapping() {
     ),
   );
 }
-
-/**
- * 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_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/lib/Drupal/rdf_test_mapping/RdfTestMappingBundle.php b/core/modules/rdf/tests/rdf_test_mapping_event/lib/Drupal/rdf_test_mapping_event/RdfTestMappingEventBundle.php
similarity index 65%
rename from core/modules/rdf/tests/rdf_test_mapping/lib/Drupal/rdf_test_mapping/RdfTestMappingBundle.php
rename to core/modules/rdf/tests/rdf_test_mapping_event/lib/Drupal/rdf_test_mapping_event/RdfTestMappingEventBundle.php
index 3de3980..0bd0c4e 100644
--- a/core/modules/rdf/tests/rdf_test_mapping/lib/Drupal/rdf_test_mapping/RdfTestMappingBundle.php
+++ b/core/modules/rdf/tests/rdf_test_mapping_event/lib/Drupal/rdf_test_mapping_event/RdfTestMappingEventBundle.php
@@ -5,7 +5,7 @@
  * Contains RdfTestMappingBundle.
  */
 
-namespace Drupal\rdf_test_mapping;
+namespace Drupal\rdf_test_mapping_event;
 
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\HttpKernel\Bundle\Bundle;
@@ -13,14 +13,14 @@
 /**
  * RDF dependency injection container.
  */
-class RdfTestMappingBundle extends Bundle {
+class RdfTestMappingEventBundle 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')
+    $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/rdf_test_mapping.info b/core/modules/rdf/tests/rdf_test_mapping_event/rdf_test_mapping_event.info
similarity index 76%
rename from core/modules/rdf/tests/rdf_test_mapping/rdf_test_mapping.info
rename to core/modules/rdf/tests/rdf_test_mapping_event/rdf_test_mapping_event.info
index d04be41..3b34e19 100644
--- a/core/modules/rdf/tests/rdf_test_mapping/rdf_test_mapping.info
+++ b/core/modules/rdf/tests/rdf_test_mapping_event/rdf_test_mapping_event.info
@@ -1,4 +1,4 @@
-name = "RDF module mapping test"
+name = "RDF module mapping event test"
 description = "Test mapping subscriber for RDF mapping tests."
 package = Testing
 core = 8.x
diff --git a/core/modules/rdf/tests/rdf_test_mapping/rdf_test_mapping.module b/core/modules/rdf/tests/rdf_test_mapping_event/rdf_test_mapping_event.module
similarity index 100%
rename from core/modules/rdf/tests/rdf_test_mapping/rdf_test_mapping.module
rename to core/modules/rdf/tests/rdf_test_mapping_event/rdf_test_mapping_event.module
diff --git a/core/modules/rdf/tests/rdf_test_namespaces/config/rdf.namespace.contrib_conflict2_contrib-conflict.yml b/core/modules/rdf/tests/rdf_test_namespaces/config/rdf.namespace.contrib_conflict2_contrib-conflict.yml
new file mode 100644
index 0000000..9ebcfed
--- /dev/null
+++ b/core/modules/rdf/tests/rdf_test_namespaces/config/rdf.namespace.contrib_conflict2_contrib-conflict.yml
@@ -0,0 +1,4 @@
+nsid: contrib_conflict2_contrib-conflict
+namespace_uri: 'http://contrib-conflict2.org/'
+prefix: contrib-conflict
+module: contrib_conflict2
diff --git a/core/modules/rdf/tests/rdf_test_namespaces/config/rdf.namespace.contrib_conflict_contrib-conflict.yml b/core/modules/rdf/tests/rdf_test_namespaces/config/rdf.namespace.contrib_conflict_contrib-conflict.yml
new file mode 100644
index 0000000..b977c37
--- /dev/null
+++ b/core/modules/rdf/tests/rdf_test_namespaces/config/rdf.namespace.contrib_conflict_contrib-conflict.yml
@@ -0,0 +1,4 @@
+nsid: contrib_conflict_contrib-conflict
+namespace_uri: 'http://contrib-conflict.org/'
+prefix: contrib-conflict
+module: contrib_conflict
diff --git a/core/modules/rdf/tests/rdf_test_namespaces/config/rdf.namespace.core_conflict2_core-conflict.yml b/core/modules/rdf/tests/rdf_test_namespaces/config/rdf.namespace.core_conflict2_core-conflict.yml
new file mode 100644
index 0000000..c4aa05b
--- /dev/null
+++ b/core/modules/rdf/tests/rdf_test_namespaces/config/rdf.namespace.core_conflict2_core-conflict.yml
@@ -0,0 +1,4 @@
+nsid: core_conflict2_core-conflict
+namespace_uri: 'http://core-conflict2.org/'
+prefix: core-conflict
+module: core_conflict2
diff --git a/core/modules/rdf/tests/rdf_test_namespaces/config/rdf.namespace.no_conflict_no-conflict.yml b/core/modules/rdf/tests/rdf_test_namespaces/config/rdf.namespace.no_conflict_no-conflict.yml
new file mode 100644
index 0000000..bbc1e9d
--- /dev/null
+++ b/core/modules/rdf/tests/rdf_test_namespaces/config/rdf.namespace.no_conflict_no-conflict.yml
@@ -0,0 +1,4 @@
+nsid: no_conflict_no-conflict
+namespace_uri: 'http://no-conflict.org/'
+prefix: no-conflict
+module: no_conflict
diff --git a/core/modules/rdf/tests/rdf_test_namespaces/config/rdf.namespace.rdf_core-conflict.yml b/core/modules/rdf/tests/rdf_test_namespaces/config/rdf.namespace.rdf_core-conflict.yml
new file mode 100644
index 0000000..d96cd5e
--- /dev/null
+++ b/core/modules/rdf/tests/rdf_test_namespaces/config/rdf.namespace.rdf_core-conflict.yml
@@ -0,0 +1,4 @@
+nsid: rdf_core-conflict
+namespace_uri: 'http://core-conflict.org/'
+prefix: core-conflict
+module: rdf
diff --git a/core/modules/rdf/tests/rdf_test_namespaces/config/rdf.namespace.rdf_test_mapping_schema.yml b/core/modules/rdf/tests/rdf_test_namespaces/config/rdf.namespace.rdf_test_mapping_schema.yml
new file mode 100644
index 0000000..4c8721d
--- /dev/null
+++ b/core/modules/rdf/tests/rdf_test_namespaces/config/rdf.namespace.rdf_test_mapping_schema.yml
@@ -0,0 +1,4 @@
+nsid: rdf_test_mapping_schema
+namespace_uri: 'http://test.org/'
+prefix: schema
+module: rdf_test_mapping
diff --git a/core/modules/rdf/tests/rdf_test_namespaces/config/rdf.namespace.rdf_test_mapping_test_prefix.yml b/core/modules/rdf/tests/rdf_test_namespaces/config/rdf.namespace.rdf_test_mapping_test_prefix.yml
new file mode 100644
index 0000000..5291615
--- /dev/null
+++ b/core/modules/rdf/tests/rdf_test_namespaces/config/rdf.namespace.rdf_test_mapping_test_prefix.yml
@@ -0,0 +1,4 @@
+nsid: rdf_test_mapping_test_prefix
+namespace_uri: 'http://test-prefix.org/'
+prefix: test_prefix
+module: rdf_test_mapping
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 16d1019..af46acb 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -1328,30 +1328,6 @@ function taxonomy_rdf_mapping() {
         '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'),
-        ),
       ),
     ),
   );
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 4500c9b..86a1abc 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -2646,28 +2646,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 8802bbc..b5c7219 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);
@@ -266,4 +238,95 @@ 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'),
+      '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'),
+    'type' => 'rel',
+  );
+  $field_tags_mapping = array(
+    'properties' => array('dc:subject'),
+    '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();
+
+  // 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'), '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();
 }
