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 c4422aa..3cbfae4 100644
--- a/core/modules/jsonld/lib/Drupal/jsonld/Tests/RdfSchemaSerializationTest.php
+++ b/core/modules/jsonld/lib/Drupal/jsonld/Tests/RdfSchemaSerializationTest.php
@@ -46,7 +46,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 9e8fbd3..1ecd4a1 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -822,53 +822,6 @@ function node_type_set_defaults($info = array()) {
 }
 
 /**
- * Implements hook_rdf_mapping().
- */
-function node_rdf_mapping() {
-  return array(
-    array(
-      'type' => 'node',
-      'bundle' => RDF_DEFAULT_BUNDLE,
-      'mapping' => array(
-        'rdftype' => array('sioc:Item', 'foaf:Document'),
-        'title' => array(
-          'predicates' => array('dc:title'),
-        ),
-        'created' => array(
-          'predicates' => array('dc:date', 'dc:created'),
-          'datatype' => 'xsd:dateTime',
-          'callback' => 'date_iso8601',
-        ),
-        'changed' => array(
-          'predicates' => array('dc:modified'),
-          'datatype' => 'xsd:dateTime',
-          'callback' => 'date_iso8601',
-        ),
-        'body' => array(
-          'predicates' => array('content:encoded'),
-        ),
-        'uid' => array(
-          'predicates' => array('sioc:has_creator'),
-          'type' => 'rel',
-        ),
-        'name' => array(
-          'predicates' => array('foaf:name'),
-        ),
-        'comment_count' => array(
-          'predicates' => array('sioc:num_replies'),
-          'datatype' => 'xsd:integer',
-        ),
-        'last_activity' => array(
-          'predicates' => array('sioc:last_activity_date'),
-          'datatype' => 'xsd:dateTime',
-          'callback' => 'date_iso8601',
-        ),
-      ),
-    ),
-  );
-}
-
-/**
  * Determines whether a node hook exists.
  *
  * @param string $type
diff --git a/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/MappingSubscriber.php b/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/MappingSubscriber.php
index 764c2f9..82f4512 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,41 @@ 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 = config($term_schema->getMappingConfigName());
+    $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 = config($term_schema->getMappingConfigName());
+    $curies = $config->get('properties');
+    if (!empty($curies)) {
+      $event->addPredicates($curies);
+    }
+  }
+
   /**
    * 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/BundleRdfMappingStorageController.php b/core/modules/rdf/lib/Drupal/rdf/Mapping/BundleRdfMappingStorageController.php
new file mode 100644
index 0000000..01ca528
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Mapping/BundleRdfMappingStorageController.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Contains BundleRdfMappingStorageController.
+ */
+
+namespace Drupal\rdf\Mapping;
+
+use Drupal\rdf\SiteSchema\SiteSchema;
+
+/**
+ * Controller class for bundle RDF mapping configuration entities.
+ */
+class BundleRdfMappingStorageController extends RdfMappingStorageControllerBase {
+
+  /**
+   * Implements \Drupal\rdf\Mapping\RdfMappingStorageControllerBase::getTermSchema().
+   */
+  protected function getTermSchema($entity) {
+    // @todo Add exception for required properties.
+    return $this->siteSchema->bundle($entity->entity_type, $entity->bundle);
+  }
+
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/Mapping/FieldRdfMappingStorageController.php b/core/modules/rdf/lib/Drupal/rdf/Mapping/FieldRdfMappingStorageController.php
new file mode 100644
index 0000000..8ed5bc2
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Mapping/FieldRdfMappingStorageController.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\rdf\Mapping\FieldRdfMappingStorageController.
+ */
+
+namespace Drupal\rdf\Mapping;
+
+use Drupal\Core\Config\Entity\ConfigStorageController;
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Controller class for field RDF mapping configuration entities.
+ */
+class FieldRdfMappingStorageController extends RdfMappingStorageControllerBase {
+
+  /**
+   * Implements \Drupal\rdf\Mapping\RdfMappingStorageControllerBase::getTermSchema().
+   */
+  protected function getTermSchema($entity) {
+    // @todo Add exception for required properties.
+    return $this->siteSchema->field($entity->entity_type, $entity->bundle, $entity->field_name);
+  }
+
+}
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..7b95b25
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Mapping/MapFieldForOutputEvent.php
@@ -0,0 +1,71 @@
+<?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 $datatype_callback;
+
+  /**
+   * 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
+   *
+   * @todo Add namespace IDs.
+   */
+  public function addPredicates($curies) {
+    if (!is_array($curies)) {
+      $curies = array($curies);
+    }
+    $this->predicates = array_merge($this->predicates, $curies);
+  }
+
+  public function getPredicates() {
+    return $this->predicates;
+  }
+
+  public function getDatatype() {
+    return $this->datatype;
+  }
+
+  public function getDatatypeCallback() {
+    return $this->datatype_callback;
+  }
+}
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..d9c5c73
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Mapping/RdfMappingManager.php
@@ -0,0 +1,242 @@
+<?php
+
+/**
+ * @file
+ * Contains RdfMappingManager.
+ */
+
+namespace Drupal\rdf\Mapping;
+
+use ReflectionClass;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\rdf\Mapping\MapTypesFromInputEvent;
+use Drupal\rdf\Mapping\MapBundleForOutputEvent;
+use Drupal\rdf\Mapping\MapFieldForOutputEvent;
+use Drupal\rdf\Mapping\RdfMappingEvents;
+use Drupal\rdf\SiteSchema\BundleSchema;
+use Drupal\rdf\SiteSchema\SiteSchema;
+use Drupal\rdf\SiteSchema\SiteSchemaManager;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+
+/**
+ * Manager for mapping internal and external schema terms.
+ */
+class RdfMappingManager {
+
+  /**
+   * The event dispatcher.
+   *
+   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+   */
+  protected $dispatcher;
+
+  /**
+   * The site schema manager.
+   *
+   * @var \Drupal\rdf\SiteSchema\SiteSchemaManager
+   */
+  protected $siteSchemaManager;
+
+  /**
+   * Constructor.
+   *
+   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
+   *   The event dispatcher.
+   * @param \Drupal\rdf\SiteSchema\SiteSchemaManager $site_schema_manager
+   *   The site schema manager.
+   */
+  public function __construct(EventDispatcherInterface $dispatcher, SiteSchemaManager $site_schema_manager) {
+    $this->dispatcher = $dispatcher;
+    $this->siteSchemaManager = $site_schema_manager;
+  }
+
+  /**
+   * Convert an array of RDF type URIs to the corresponding TypedData IDs.
+   *
+   * @param array $input_rdf_types
+   *   An array of URIs for the type.
+   *
+   * @return array
+   *   An array containing entity_type and bundle.
+   *
+   * @throws \Drupal\rdf\Mapping\RdfMappingException
+   */
+  public function getTypedDataIdsFromTypeUris($input_rdf_types) {
+    // Get the cache of site schema types.
+    $site_schema_types = $this->siteSchemaManager->getTypes();
+    // Map the RDF type from the incoming data to an RDF type defined in the
+    // internal site schema.
+    $type_uri = $this->mapTypesFromInput($input_rdf_types);
+    // If no site schema URI has been determined, then it's impossible to know
+    // what entity type to create. Throw an exception.
+    if ($type_uri == FALSE) {
+      throw new RdfMappingException(sprintf('No mapping to a site schema type URI found for incoming types (%s).', implode(',', $input_rdf_types)));
+    }
+    // Use the mapped RDF type URI to get the TypedData API ids the rest of the
+    // system uses (entity type and bundle).
+    return $site_schema_types[$type_uri];
+  }
+
+  /**
+   * Convert a bundle's Typed Data IDs to an RDF mapping array.
+   *
+   * Bundles will have different mappings based on whether the data is to be
+   * used for content deployment or for syndication. The site schema id is used
+   * to specify which to return.
+   *
+   * @param string $entity_type
+   *   The entity type of the bundle which the mapping corresponds to.
+   * @param string $bundle
+   *   The name of the bundle which the mapping corresponds to.
+   * @param string $schema_id
+   *   The id of the site schema which is being mapped to.
+   *
+   * @return array
+   *   The RDF mapping.
+   */
+  public function getBundleMapping($entity_type, $bundle, $schema_id = SiteSchema::SYNDICATION) {
+    $site_schema = $this->siteSchemaManager->getSchema($schema_id);
+    $term_schema = $site_schema->bundle($entity_type, $bundle);
+    // Get the mapping, which contains the RDF types used when publishing data.
+    return $this->mapBundleForOutput($term_schema);
+  }
+
+  /**
+   * Convert a field instance's Typed Data IDs to an RDF mapping array.
+   *
+   * Field instances will have different mappings based on whether the data is
+   * to be used for content deployment or for syndication. The site schema id
+   * is used to specify which to return.
+   *
+   * @param string $entity_type
+   *   The entity type of the field instance which the mapping corresponds to.
+   * @param string $bundle
+   *   The bundle of the field instance which the mapping corresponds to.
+   * @param string field_name
+   *   The field name of the field instance which the mapping corresponds to.
+   * @param string $schema_id
+   *   The id of the site schema which is being mapped to.
+   *
+   * @return array
+   *   The RDF mapping.
+   */
+  public function getFieldMapping($entity_type, $bundle, $field_name, $schema_id = SiteSchema::SYNDICATION) {
+    $site_schema = $this->siteSchemaManager->getSchema($schema_id);
+    $term_schema = $site_schema->field($entity_type, $bundle, $field_name);
+    return $this->mapFieldForOutput($term_schema);
+  }
+
+  /**
+   * Get the mapping config entity for a bundle.
+   *
+   * Bundles will have different mappings based on whether the data is to be
+   * used for content deployment or for syndication. The site schema id is used
+   * to specify which to return.
+   *
+   * @param string $entity_type
+   *   The entity type of the bundle which the mapping corresponds to.
+   * @param string $bundle
+   *   The name of the bundle which the mapping corresponds to.
+   * @param string $schema_id
+   *   The id of the site schema which is being mapped to.
+   *
+   * @return array
+   *   The RDF mapping config entity.
+   */
+  public function getBundleMappingConfig($entity_type, $bundle, $schema_id = SiteSchema::SYNDICATION) {
+    $bundle_schema = $this->siteSchemaManager->getSchema($schema_id)->bundle($entity_type, $bundle);
+    return $bundle_schema->getMappingConfig();
+  }
+
+  /**
+   * Get the field mapping config entity for a field instance.
+   *
+   * Field instances will have different mappings based on whether the data is
+   * to be used for content deployment or for syndication. The site schema id
+   * is used to specify which to return.
+   *
+   * @param string $entity_type
+   *   The entity type of the field instance which the mapping corresponds to.
+   * @param string $bundle
+   *   The bundle of the field instance which the mapping corresponds to.
+   * @param string field_name
+   *   The field name of the field instance which the mapping corresponds to.
+   * @param string $schema_id
+   *   The id of the site schema which is being mapped to.
+   *
+   * @return array
+   *   The RDF mapping.
+   */
+  public function getFieldMappingConfig($entity_type, $bundle, $field_name, $schema_id = SiteSchema::SYNDICATION) {
+    $field_schema = $this->siteSchemaManager->getSchema($schema_id)->field($entity_type, $bundle, $field_name);
+    return $field_schema->getMappingConfig();
+  }
+
+  /**
+   * Map an array of incoming URIs to an internal site schema URI.
+   *
+   * @param array $input_rdf_types
+   *   An array of RDF type URIs.
+   *
+   * @return string
+   *   The corresponding site schema type URI.
+   */
+  protected function mapTypesFromInput($input_rdf_types) {
+    // Create the event using the array of incoming RDF type URIs and the cache
+    // of internal site schema URIs.
+    $site_schema_types = $this->siteSchemaManager->getTypes();
+    $mapping_event = new MapTypesFromInputEvent($input_rdf_types, $site_schema_types);
+
+    // Allow other modules to map the incoming type URIs to an internal site
+    // schema type URI. For example, a content deployment module could take
+    // URIs from the staging site's schema and map them to the corresponding
+    // URI in the live site's schema.
+    $this->dispatcher->dispatch(RdfMappingEvents::MAP_TYPES_FROM_INPUT, $mapping_event);
+
+    return $mapping_event->getSiteSchemaUri();
+  }
+
+  /**
+   * Map a bundle's site schema term to external vocabularies.
+   *
+   * @param \Drupal\rdf\SiteSchema\SchemaTermInterface $term_schema
+   *   The term object.
+   *
+   * @return string
+   *   The corresponding RDF mapping.
+   */
+  protected function mapBundleForOutput($term_schema) {
+    $mapping = array();
+    $map_event = new MapBundleForOutputEvent($term_schema);
+    $this->dispatcher->dispatch(RdfMappingEvents::MAP_BUNDLE_FOR_OUTPUT, $map_event);
+    $types = $map_event->getTypes();
+    if (!empty($types)) {
+      $mapping['types'] = $types;
+    }
+    return $mapping;
+  }
+
+  /**
+   * Map a field instance's site schema term to external vocabularies.
+   *
+   * @param \Drupal\rdf\SiteSchema\SchemaTermInterface $term_schema
+   *   The term object.
+   *
+   * @return string
+   *   The corresponding RDF mapping.
+   */
+  protected function mapFieldForOutput($term_schema) {
+    $mapping = array();
+    $map_event = new MapFieldForOutputEvent($term_schema);
+    $this->dispatcher->dispatch(RdfMappingEvents::MAP_FIELD_FOR_OUTPUT, $map_event);
+
+    $predicates = $map_event->getPredicates();
+    if (!empty($predicates)) {
+      $mapping['properties'] = $predicates;
+      $mapping['datatype'] = $map_event->getDatatype();
+      $mapping['datatype_callback'] = $map_event->getDatatypeCallback();
+    }
+    return array_filter($mapping);
+  }
+
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/Mapping/RdfMappingStorageControllerBase.php b/core/modules/rdf/lib/Drupal/rdf/Mapping/RdfMappingStorageControllerBase.php
new file mode 100644
index 0000000..8129c85
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Mapping/RdfMappingStorageControllerBase.php
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * @file
+ * Contains RdfMappingStorageControllerBase.
+ */
+
+namespace Drupal\rdf\Mapping;
+
+use Drupal\Core\Config\Entity\ConfigStorageController;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\rdf\SiteSchema\SiteSchema;
+use Drupal\rdf\SiteSchema\SiteSchemaManager;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+abstract class RdfMappingStorageControllerBase extends ConfigStorageController {
+
+  protected $siteSchema;
+
+  /**
+   * Overrides Drupal\Core\Entity\ConfigStorageController::__construct().
+   *
+   * Sets basic variables.
+   */
+  public function __construct($entityType) {
+    parent::__construct($entityType);
+    $this->siteSchema = drupal_container()->get('rdf.site_schema_manager')->getSchema(SiteSchema::SYNDICATION);
+  }
+
+  /**
+   * Overrides \Drupal\Core\Config\Entity\ConfigStorageController::save().
+   */
+  public function save(EntityInterface $entity) {
+    if (!isset($entity->mid)) {
+      $entity->mid = $this->getTermSchema($entity)->getMappingConfigId();
+    }
+    parent::save($entity);
+  }
+
+  /**
+   * Get the site schema term object that is being mapped.
+   *
+   * This is used by the storage controller to determine what the name of the
+   * mapping config object should be.
+   *
+   * @param \Drupal\Core\Entity\Entity $entity
+   *   The config entity.
+   *
+   * @return \Drupal\rdf\SiteSchema\SchemaTermInterface
+   *   The site schema term object.
+   */
+  abstract protected function getTermSchema($entity);
+
+}
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..ec30afd
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/BundleRdfMapping.php
@@ -0,0 +1,74 @@
+<?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\rdf\BundleRdfMappingStorageController",
+ *   form_controller_class = {
+ *     "default" = "Drupal\Core\Entity\EntityFormController"
+ *   },
+ *   config_prefix = "rdf.mapping.bundle",
+ *   entity_keys = {
+ *     "id" = "mid",
+ *   }
+ * )
+ */
+class BundleRdfMapping extends ConfigEntityBase {
+
+  /**
+   * The RDF mapping ID.
+   *
+   * @var string
+   */
+  public $mid;
+
+  /**
+   * The schema of the mapped site schema term.
+   *
+   * @var string
+   */
+  public $schema_id;
+
+  /**
+   * The entity type of the corresponding bundle.
+   *
+   * @var string
+   */
+  public $entity_type;
+
+  /**
+   * The site schema term's corresponding bundle.
+   *
+   * @var string
+   */
+  public $bundle;
+
+  /**
+   * The types that are being mapped to this site schema term.
+   *
+   * @var array
+   */
+  public $types;
+
+  /**
+   * Implements Drupal\Core\Entity\EntityInterface::id().
+   */
+  public function id() {
+    return $this->mid;
+  }
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/FieldRdfMapping.php b/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/FieldRdfMapping.php
new file mode 100644
index 0000000..7c1dc1c
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/FieldRdfMapping.php
@@ -0,0 +1,95 @@
+<?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\rdf\FieldRdfMappingStorageController",
+ *   form_controller_class = {
+ *     "default" = "Drupal\Core\Entity\EntityFormController"
+ *   },
+ *   config_prefix = "rdf.mapping.field",
+ *   entity_keys = {
+ *     "id" = "mid",
+ *   }
+ * )
+ */
+class FieldRdfMapping extends ConfigEntityBase {
+
+  /**
+   * The RDF mapping ID.
+   *
+   * @var string
+   */
+  public $mid;
+
+  /**
+   * The schema of the mapped site schema term.
+   *
+   * @var string
+   */
+  public $schema_id;
+
+  /**
+   * The entity type of the bundle that the corresponding field is attached to.
+   *
+   * @var string
+   */
+  public $entity_type;
+
+  /**
+   * The bundle that the corresponding field is attached to.
+   *
+   * @var string
+   */
+  public $bundle;
+
+  /**
+   * The site schema term's corresponding field.
+   *
+   * @var string
+   */
+  public $field_name;
+
+  /**
+   * The RDF properties the field is mapped to.
+   *
+   * @var array
+   */
+  public $properties;
+
+  /**
+   * If this is a primitive, the datatype URI.
+   *
+   * @var string
+   */
+  public $datatype;
+
+  /**
+   * If this is a primitive, the callback to get a properly formatted value.
+   *
+   * @var callable
+   */
+  public $datatype_callback;
+
+  /**
+   * Implements Drupal\Core\Entity\EntityInterface::id().
+   */
+  public function id() {
+    return $this->mid;
+  }
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/RdfBundle.php b/core/modules/rdf/lib/Drupal/rdf/RdfBundle.php
index 40affcc..295ebfc 100644
--- a/core/modules/rdf/lib/Drupal/rdf/RdfBundle.php
+++ b/core/modules/rdf/lib/Drupal/rdf/RdfBundle.php
@@ -30,7 +30,7 @@ public function build(ContainerBuilder $container) {
     $container->register('rdf.site_schema_manager', 'Drupal\rdf\SiteSchema\SiteSchemaManager')
       ->addArgument(new Reference('cache.rdf.site_schema.types'));
     // Mapping manager service.
-    $container->register('rdf.mapping_manager', 'Drupal\rdf\RdfMappingManager')
+    $container->register('rdf.mapping_manager', 'Drupal\rdf\Mapping\RdfMappingManager')
       ->addArgument(new Reference('event_dispatcher'))
       ->addArgument(new Reference('rdf.site_schema_manager'));
 
diff --git a/core/modules/rdf/lib/Drupal/rdf/RdfConstants.php b/core/modules/rdf/lib/Drupal/rdf/RdfConstants.php
index b64ae91..4ac035f 100644
--- a/core/modules/rdf/lib/Drupal/rdf/RdfConstants.php
+++ b/core/modules/rdf/lib/Drupal/rdf/RdfConstants.php
@@ -12,8 +12,9 @@
  */
 abstract class RdfConstants {
   const RDF_TYPE            = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type';
+  const RDF_PROPERTY        = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Property';
   // RDF Schema terms.
-  const RDFS_CLASS          = 'http://www.w3.org/2000/01/rdf-schema#class';
+  const RDFS_CLASS          = 'http://www.w3.org/2000/01/rdf-schema#Class';
   const RDFS_DOMAIN         = 'http://www.w3.org/2000/01/rdf-schema#domain';
   const RDFS_IS_DEFINED_BY  = 'http://www.w3.org/2000/01/rdf-schema#isDefinedBy';
   const RDFS_RANGE          = 'http://www.w3.org/2000/01/rdf-schema#range';
diff --git a/core/modules/rdf/lib/Drupal/rdf/RdfMappingEvents.php b/core/modules/rdf/lib/Drupal/rdf/RdfMappingEvents.php
deleted file mode 100644
index 0e3fdae..0000000
--- a/core/modules/rdf/lib/Drupal/rdf/RdfMappingEvents.php
+++ /dev/null
@@ -1,29 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains RdfMappingEvents.
- */
-
-namespace Drupal\rdf;
-
-/**
- * Contains all events for mapping site schemas to external vocabularies.
- */
-final class RdfMappingEvents {
-
-  /**
-   * Maps an array of incoming type URIs to a site schema URI.
-   *
-   * Modules can use this event to convert an RDF type from an externally
-   * defined vocabulary to a URI defined in the site's schema. From the site
-   * schema URI, the site can derive the Typed Data API ids, which can be used
-   * to create an entity.
-   *
-   * @see \Drupal\rdf\RdfMappingManager
-   *
-   * @var string
-   */
-  const MAP_TYPES_FROM_INPUT = 'rdf.map_types_from_input';
-
-}
diff --git a/core/modules/rdf/lib/Drupal/rdf/RdfMappingManager.php b/core/modules/rdf/lib/Drupal/rdf/RdfMappingManager.php
deleted file mode 100644
index 90588e9..0000000
--- a/core/modules/rdf/lib/Drupal/rdf/RdfMappingManager.php
+++ /dev/null
@@ -1,101 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains RdfMappingManager.
- */
-
-namespace Drupal\rdf;
-
-use ReflectionClass;
-use Drupal\Core\Cache\CacheBackendInterface;
-use Drupal\rdf\MapTypesFromInputEvent;
-use Drupal\rdf\RdfMappingEvents;
-use Drupal\rdf\SiteSchema\BundleSchema;
-use Drupal\rdf\SiteSchema\SiteSchema;
-use Drupal\rdf\SiteSchema\SiteSchemaManager;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-
-/**
- * Manager for mapping internal and external schema terms.
- */
-class RdfMappingManager {
-
-  /**
-   * The event dispatcher.
-   *
-   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
-   */
-  protected $dispatcher;
-
-  /**
-   * The site schema manager.
-   *
-   * @var \Drupal\rdf\SiteSchema\SiteSchemaManager
-   */
-  protected $siteSchemaManager;
-
-  /**
-   * Constructor.
-   *
-   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
-   *   The event dispatcher.
-   * @param \Drupal\rdf\SiteSchema\SiteSchemaManager $site_schema_manager
-   *   The site schema manager.
-   */
-  public function __construct(EventDispatcherInterface $dispatcher, SiteSchemaManager $site_schema_manager) {
-    $this->dispatcher = $dispatcher;
-    $this->siteSchemaManager = $site_schema_manager;
-  }
-
-  /**
-   * Convert an array of RDF type URIs to the corresponding TypedData IDs.
-   *
-   * @param array $input_rdf_types
-   *   An array of URIs for the type.
-   *
-   * @return array
-   *   An array containing entity_type and bundle.
-   *
-   * @throws \Drupal\rdf\RdfMappingException
-   */
-  public function getTypedDataIdsFromTypeUris($input_rdf_types) {
-    // Get the cache of site schema types.
-    $site_schema_types = $this->siteSchemaManager->getTypes();
-    // Map the RDF type from the incoming data to an RDF type defined in the
-    // internal site schema.
-    $type_uri = $this->mapTypesFromInput($input_rdf_types);
-    // If no site schema URI has been determined, then it's impossible to know
-    // what entity type to create. Throw an exception.
-    if ($type_uri == FALSE) {
-      throw new RdfMappingException(sprintf('No mapping to a site schema type URI found for incoming types (%s).', implode(',', $input_rdf_types)));
-    }
-    // Use the mapped RDF type URI to get the TypedData API ids the rest of the
-    // system uses (entity type and bundle).
-    return $site_schema_types[$type_uri];
-  }
-
-  /**
-   * Map an array of incoming URIs to an internal site schema URI.
-   *
-   * @param array $input_rdf_types
-   *   An array of RDF type URIs.
-   *
-   * @return string
-   *   The corresponding site schema type URI.
-   */
-  protected function mapTypesFromInput($input_rdf_types) {
-    // Create the event using the array of incoming RDF type URIs and the cache
-    // of internal site schema URIs.
-    $site_schema_types = $this->siteSchemaManager->getTypes();
-    $mapping_event = new MapTypesFromInputEvent($input_rdf_types, $site_schema_types);
-
-    // Allow other modules to map the incoming type URIs to an internal site
-    // schema type URI. For example, a content deployment module could take
-    // URIs from the staging site's schema and map them to the corresponding
-    // URI in the live site's schema.
-    $this->dispatcher->dispatch(RdfMappingEvents::MAP_TYPES_FROM_INPUT, $mapping_event);
-
-    return $mapping_event->getSiteSchemaUri();
-  }
-}
diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/BundleSchema.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/BundleSchema.php
index 2c92696..ff79559 100644
--- a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/BundleSchema.php
+++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/BundleSchema.php
@@ -22,6 +22,8 @@ class BundleSchema extends EntitySchema {
    */
   public static $uriPattern = '{entity_type}/{bundle}';
 
+  protected $configPrefix = 'rdf.mapping.bundle';
+
   /**
    * The bundle that this term identifies.
    *
@@ -45,11 +47,14 @@ public function __construct($site_schema, $entity_type, $bundle) {
   }
 
   /**
-   * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getUri().
+   * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getPath().
    */
-  public function getUri() {
-    $path = str_replace(array('{entity_type}', '{bundle}'), array($this->entityType, $this->bundle), static::$uriPattern);
-    return $this->siteSchema->getUri() . $path;
+  public function getPath() {
+    return $this->siteSchema->getPath() . $this->prepareUriPattern();
+  }
+
+  protected function prepareUriPattern() {
+    return str_replace(array('{entity_type}', '{bundle}'), array($this->entityType, $this->bundle), static::$uriPattern);
   }
 
   /**
@@ -61,4 +66,10 @@ public function getProperties() {
     return $properties;
   }
 
+  public function getTermIds() {
+    $keys = parent::getTermIds();
+    $keys['bundle'] = $this->bundle;
+    return $keys;
+  }
+
 }
diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/EntitySchema.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/EntitySchema.php
index 48a69fc..5d4dd7b 100644
--- a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/EntitySchema.php
+++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/EntitySchema.php
@@ -54,11 +54,11 @@ public function getGraph() {
   }
 
   /**
-   * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getUri().
+   * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getPath().
    */
-  public function getUri() {
+  public function getPath() {
     $path = str_replace('{entity_type}', $this->entityType , static::$uriPattern);
-    return $this->siteSchema->getUri() . $path;
+    return $this->siteSchema->getPath() . $path;
   }
 
   /**
@@ -70,4 +70,11 @@ public function getProperties() {
     return $properties;
   }
 
+  public function getTermIds() {
+    return array(
+      'schema_id' => $this->siteSchema->getId(),
+      'entity_type' => $this->entityType,
+    );
+  }
+
 }
diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/FieldSchema.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/FieldSchema.php
new file mode 100644
index 0000000..e7c9425
--- /dev/null
+++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/FieldSchema.php
@@ -0,0 +1,105 @@
+<?php
+
+/**
+ * @file
+ * Contains FieldSchema.
+ */
+
+namespace Drupal\rdf\SiteSchema;
+
+use Drupal\rdf\RdfConstants;
+use Drupal\rdf\SiteSchema\SchemaTermBase;
+
+/**
+ * Defines RDF terms corresponding to Drupal field instances.
+ */
+class FieldSchema extends SchemaTermBase {
+
+  /**
+   * The URI pattern for entity site schema terms.
+   *
+   * @var string
+   */
+  public static $uriPattern = '{entity_type}/{bundle}/{field_name}';
+
+  protected $configPrefix = 'rdf.mapping.field';
+
+  /**
+   * The entity type that this term identifies.
+   *
+   * @var string
+   */
+  protected $entityType;
+
+  /**
+   * The entity type that this term identifies.
+   *
+   * @var string
+   */
+  protected $bundle;
+
+  /**
+   * The entity type that this term identifies.
+   *
+   * @var string
+   */
+  protected $fieldName;
+
+  /**
+   * Constructor.
+   *
+   * @param \Drupal\rdf\SiteSchema\SiteSchema $siteSchema
+   *   The schema the term is defined in.
+   * @param string $entity_type
+   *   The entity type of the bundle the field is attached to.
+   * @param $bundle
+   *   The bundle the field is attached to.
+   * @param $field_name
+   *   The field name.
+   */
+  public function __construct($siteSchema, $entity_type, $bundle, $field_name) {
+    parent::__construct($siteSchema);
+    $this->entityType = $entity_type;
+    $this->bundle = $bundle;
+    $this->fieldName = $field_name;
+  }
+
+  /**
+   * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getGraph().
+   *
+   * @todo Loop through all fields and add their RDF descriptions.
+   */
+  public function getGraph() {
+    // @todo Implement this.
+  }
+
+  /**
+   * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getPath().
+   */
+  public function getPath() {
+    return $this->siteSchema->getPath() . $this->prepareUriPattern();
+  }
+
+  protected function prepareUriPattern() {
+    return str_replace(array('{entity_type}', '{bundle}', '{field_name}'), array($this->entityType, $this->bundle, $this->fieldName), static::$uriPattern);
+  }
+
+  /**
+   * Overrides \Drupal\rdf\SiteSchema\SchemaTermBase::getProperties().
+   */
+  public function getProperties() {
+    $properties = parent::getProperties();
+    $properties[RdfConstants::RDF_TYPE] = RdfConstants::RDF_PROPERTY;
+    return $properties;
+  }
+
+  public function getTermIds() {
+    return array(
+      'schema_id' => $this->siteSchema->getId(),
+      'entity_type' => $this->entityType,
+      'bundle' => $this->bundle,
+      'field_name' => $this->fieldName,
+    );
+  }
+
+}
diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaController.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaController.php
index b60eb84..9617401 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..7515b16 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,26 @@ public function getProperties() {
     );
   }
 
+  public function getMappingConfig() {
+    $config_name = $this->getMappingConfigName();
+    $config = config($config_name);
+
+    // 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->mid = $config_name;
+      foreach ($this->getTermIds() as $key => $value) {
+        $config->set($key, $value);
+      }
+      $config->save();
+    }
+
+    return $config;
+  }
+
+  public function getMappingConfigName() {
+    return $this->configPrefix . '.' . implode(':', $this->getTermIds());
+  }
+
 }
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 7ca9ca8..6caa8fd 100644
--- a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchemaManager.php
+++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchemaManager.php
@@ -65,8 +65,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..3fafc3b 100644
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/CrudTest.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/CrudTest.php
@@ -19,7 +19,7 @@ class CrudTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('rdf', 'rdf_test');
+  public static $modules = array('rdf', 'entity_test');
 
   public static function getInfo() {
     return array(
@@ -33,53 +33,54 @@ 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.');
 
-    // 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/RdfMappingEventTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/RdfMappingEventTest.php
index 7a7a7b5..d50d953 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\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', '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', '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/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
index 10d3f8d..be2cde1 100644
--- a/core/modules/rdf/rdf.install
+++ b/core/modules/rdf/rdf.install
@@ -4,46 +4,3 @@
  * @file
  * Install, update and uninstall functions for the rdf module.
  */
-
-/**
- * Implements hook_schema().
- */
-function rdf_schema() {
-  $schema['rdf_mapping'] = array(
-    'description' => 'Stores custom RDF mappings for user defined content types or overriden module-defined mappings',
-    'fields' => array(
-      'type' => array(
-        'type' => 'varchar',
-        'length' => 128,
-        'not null' => TRUE,
-        'description' => 'The name of the entity type a mapping applies to (node, user, comment, etc.).',
-      ),
-      'bundle' => array(
-        'type' => 'varchar',
-        'length' => 128,
-        'not null' => TRUE,
-        'description' => 'The name of the bundle a mapping applies to.',
-      ),
-      'mapping' => array(
-        'description' => 'The serialized mapping of the bundle type and fields to RDF terms.',
-        'type' => 'blob',
-        'not null' => FALSE,
-        'size' => 'big',
-        'serialize' => TRUE,
-      ),
-    ),
-    'primary key' => array('type', 'bundle'),
-  );
-
-  return $schema;
-}
-
-/**
- * Implements hook_install().
- */
-function rdf_install() {
-  // Collect any RDF mappings that were declared by modules installed before
-  // this one.
-  $modules = module_implements('rdf_mapping');
-  rdf_modules_installed($modules);
-}
diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module
index ffc3a0b..d88e253 100644
--- a/core/modules/rdf/rdf.module
+++ b/core/modules/rdf/rdf.module
@@ -67,16 +67,6 @@ function rdf_help($path, $arg) {
  */
 
 /**
- * RDF bundle flag: Default bundle.
- *
- * Implementations of hook_rdf_mapping() should use this constant for the
- * 'bundle' key when defining a default set of RDF mappings for an entity type.
- * Each bundle will inherit the default mappings defined for the entity type
- * unless the bundle defines its own specific mappings.
- */
-const RDF_DEFAULT_BUNDLE = '';
-
-/**
  * Implements hook_rdf_namespaces().
  */
 function rdf_rdf_namespaces() {
@@ -118,100 +108,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.
-  $entity_info = entity_get_info($type);
-  if (!empty($entity_info['bundles'][$bundle]['rdf_mapping'])) {
-    return $entity_info['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();
-}
-
-/**
- * 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;
+  return $mapping;
 }
 
 /**
- * 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 +143,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 +178,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,98 +204,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_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_info_alter(&$entity_info) {
-  // Loop through each entity type and its bundles.
-  foreach ($entity_info as $entity_type => $entity_type_info) {
-    if (!empty($entity_type_info['bundles'])) {
-      $bundles = array_keys($entity_type_info['bundles']);
-      $mappings = _rdf_mapping_load_multiple($entity_type, $bundles);
-
-      foreach ($bundles as $bundle) {
-        if (isset($mappings[$bundle])) {
-          $entity_info[$entity_type]['bundles'][$bundle]['rdf_mapping'] = $mappings[$bundle];
-        }
-        else {
-          // If no mapping was found in the database, assign the default RDF
-          // mapping for this entity type.
-          $entity_info[$entity_type]['bundles'][$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);
-    $comment->rdf_data['nid_uri'] = url('node/' . $comment->nid);
-    if ($comment->pid) {
-      $comment->rdf_data['pid_uri'] = url('comment/' . $comment->pid, array('fragment' => 'comment-' . $comment->pid));
-    }
-  }
-}
-
-/**
  * Implements hook_theme().
  */
 function rdf_theme() {
@@ -501,14 +278,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
@@ -521,35 +301,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
@@ -566,9 +349,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');
@@ -610,6 +393,8 @@ 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.
@@ -629,11 +414,12 @@ function rdf_preprocess_user(&$variables) {
     ),
   );
   // 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' => $account->rdf_mapping['name']['predicates'],
+      'property' => $name_mapping['properties'],
       'content' => $account->name,
     )
   );
@@ -682,11 +468,11 @@ function rdf_preprocess_username(&$variables) {
   // Annotate the user name in RDFa. The property attribute is used here
   // because the user name is a literal.
   if (!empty($rdf_mapping['name'])) {
-    $attributes['property'] = $rdf_mapping['name']['predicates'];
+    $attributes['property'] = $rdf_mapping['name']['properties'];
   }
   // 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
@@ -721,15 +507,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'] = '';
   }
 
@@ -737,7 +523,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'];
@@ -745,7 +531,7 @@ function rdf_preprocess_comment(&$variables) {
 
     // Adds the relation to parent comment, if it exists.
     if ($comment->pid != 0) {
-      $parent_comment_attributes['rel'] = $comment->rdf_mapping['pid']['predicates'];
+      $parent_comment_attributes['rel'] = $comment->rdf_mapping['pid']['properties'];
       // The parent comment URI is precomputed as part of the rdf_data so that
       // it can be cached as part of the entity.
       $parent_comment_attributes['resource'] = $comment->rdf_data['pid_uri'];
@@ -765,7 +551,7 @@ function rdf_preprocess_taxonomy_term(&$variables) {
       '#attributes' => array(
         'about' => url('taxonomy/term/' . $term->tid),
         'typeof' => $term->rdf_mapping['rdftype'],
-        'property' => $term->rdf_mapping['name']['predicates'],
+        'property' => $term->rdf_mapping['name']['properties'],
         'content' => $term->label(),
       ),
     );
@@ -789,8 +575,8 @@ function rdf_field_attach_view_alter(&$output, $context) {
           if (!empty($term->rdf_mapping['rdftype'])) {
             $element[$delta]['#options']['attributes']['typeof'] = $term->rdf_mapping['rdftype'];
           }
-          if (!empty($term->rdf_mapping['name']['predicates'])) {
-            $element[$delta]['#options']['attributes']['property'] = $term->rdf_mapping['name']['predicates'];
+          if (!empty($term->rdf_mapping['name']['properties'])) {
+            $element[$delta]['#options']['attributes']['property'] = $term->rdf_mapping['name']['properties'];
           }
         }
       }
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
index 44e9460..9c03f7e 100644
--- 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
@@ -7,7 +7,7 @@
 
 namespace Drupal\rdf_test_mapping\EventSubscriber;
 
-use Drupal\rdf\RdfMappingEvents;
+use Drupal\rdf\Mapping\RdfMappingEvents;
 use Drupal\rdf\SiteSchema\BundleSchema;
 use Drupal\rdf\SiteSchema\SiteSchema;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
@@ -15,11 +15,12 @@
 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 between external type and site schema type.
+   * Demonstrate mapping external type to site schema type on consumption.
    *
-   * @param \Drupal\rdf\MapTypesFromInputEvent $event
+   * @param \Drupal\rdf\Mapping\MapTypesFromInputEvent $event
    *   The mapping event.
    */
   public function mapTypesFromInput($event) {
@@ -48,10 +49,46 @@ public function mapTypesFromInput($event) {
   }
 
   /**
+   * 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/user/user.module b/core/modules/user/user.module
index bff000f..d498450 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -2732,28 +2732,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 5cb8fb3..eb970f0 100644
--- a/core/profiles/standard/standard.install
+++ b/core/profiles/standard/standard.install
@@ -108,34 +108,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);
@@ -328,4 +300,87 @@ 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',
+  );
+  // Save field_image mapping.
+  $config = $rdf_mapping_manager->getFieldMappingConfig('node', 'article', 'field_image');
+  $field_mapping = array_merge($config->get(), $field_image_mapping);
+  $config->setData($field_mapping)->save();
+  // Save field_tags mapping.
+  $config = $rdf_mapping_manager->getFieldMappingConfig('node', 'article', 'field_tags');
+  $field_mapping = array_merge($config->get(), $field_tags_mapping);
+  $config->setData($field_mapping)->save();
+
+  // Save the User bundle RDF mapping configuration.
+  $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.
+  // Save the name RDF mapping configuration.
+  $config = $rdf_mapping_manager->getFieldMappingConfig('user', 'user', 'name');
+  $field_mapping = array_merge($config->get(), array('properties' => array('foaf:name')));
+  $config->setData($field_mapping)->save();
+  // Save the homepage RDF mapping configuration.
+  $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();
+
 }
