diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php
index e1147ac..e957a8e 100644
--- a/core/lib/Drupal/Core/CoreBundle.php
+++ b/core/lib/Drupal/Core/CoreBundle.php
@@ -233,6 +233,11 @@ public function build(ContainerBuilder $container) {
       ->addArgument(array())
       ->addArgument(array());
 
+    $container->register('serializer.normalizer.complex_data', 'Drupal\Core\Serialization\ComplexDataNormalizer')->addTag('normalizer');
+    $container->register('serializer.normalizer.list', 'Drupal\Core\Serialization\ListNormalizer')->addTag('normalizer');
+    $container->register('serializer.normalizer.typed_data', 'Drupal\Core\Serialization\TypedDataNormalizer')->addTag('normalizer');
+    $container->register('serializer.encoder.json', 'Drupal\Core\Serialization\JsonEncoder')->addTag('encoder');
+
     $container->register('flood', 'Drupal\Core\Flood\DatabaseBackend')
       ->addArgument(new Reference('database'));
 
diff --git a/core/lib/Drupal/Core/Serialization/ComplexDataNormalizer.php b/core/lib/Drupal/Core/Serialization/ComplexDataNormalizer.php
new file mode 100644
index 0000000..06a5415
--- /dev/null
+++ b/core/lib/Drupal/Core/Serialization/ComplexDataNormalizer.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Serialization\ComplexDataNormalizer.
+ */
+
+namespace Drupal\Core\Serialization;
+
+use Drupal\Core\Serialization\NormalizerBase;
+use Symfony\Component\Serializer\Exception\RuntimeException;
+
+/**
+ * Converts the Drupal entity object structures to a normalized array.
+ *
+ * This is the default Normalizer for entities. All formats that have Encoders
+ * registered with the Serializer in the DIC will be normalized with this
+ * class unless another Normalizer is registered which supersedes it. If a
+ * module wants to use format-specific or class-specific normalization, then
+ * that module can register a new Normalizer and give it a higher priority than
+ * this one.
+ */
+class ComplexDataNormalizer extends NormalizerBase {
+
+  /**
+   * The interface or class that this Normalizer supports.
+   *
+   * @var string
+   */
+  protected static $supportedInterfaceOrClass = 'Drupal\Core\TypedData\ComplexDataInterface';
+
+  /**
+   * Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize().
+   */
+  public function normalize($object, $format = NULL) {
+    $attributes = array();
+    foreach ($object as $name => $field) {
+      $attributes[$name] = $this->serializer->normalize($field, $format);
+    }
+    return $attributes;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Serialization/JsonEncoder.php b/core/lib/Drupal/Core/Serialization/JsonEncoder.php
new file mode 100644
index 0000000..8d7e3d7
--- /dev/null
+++ b/core/lib/Drupal/Core/Serialization/JsonEncoder.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\Serialization\JsonEncoder.
+ */
+
+namespace Drupal\Core\Serialization;
+
+use Symfony\Component\Serializer\Encoder\EncoderInterface;
+use Symfony\Component\Serializer\Encoder\JsonEncoder as BaseJsonEncoder;
+
+/**
+ * Adds 'ajax to the supported content types of the JSON encoder'
+ */
+class JsonEncoder extends BaseJsonEncoder implements EncoderInterface {
+
+  /**
+   * The formats that this Encoder supports.
+   *
+   * @var array
+   */
+  static protected $format = array('json', 'ajax');
+
+  /**
+   * Overrides Symfony\Component\Serializer\Encoder\JsonEncoder::supportEncoding().
+   */
+  public function supportsEncoding($format) {
+    return in_array($format, static::$format);
+  }
+}
diff --git a/core/lib/Drupal/Core/Serialization/ListNormalizer.php b/core/lib/Drupal/Core/Serialization/ListNormalizer.php
new file mode 100644
index 0000000..ee20fb6
--- /dev/null
+++ b/core/lib/Drupal/Core/Serialization/ListNormalizer.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Serialization\ListNormalizer.
+ */
+
+namespace Drupal\Core\Serialization;
+
+use Drupal\Core\Serialization\NormalizerBase;
+use Symfony\Component\Serializer\Exception\RuntimeException;
+
+/**
+ * Converts list objects to arrays.
+ *
+ * Ordinarily, this would be handled automatically by Serializer, but since
+ * there is a TypedDataNormalizer and the Field class extends TypedData, any
+ * Field will be handled by that Normalizer instead of being traversed. This
+ * class ensures that TypedData classes that also implement ListInterface are
+ * traversed instead of simply returning getValue().
+ */
+class ListNormalizer extends NormalizerBase {
+
+  /**
+   * The interface or class that this Normalizer supports.
+   *
+   * @var string
+   */
+  protected static $supportedInterfaceOrClass = 'Drupal\Core\TypedData\ListInterface';
+
+  /**
+   * Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize().
+   */
+  public function normalize($object, $format = NULL) {
+    $attributes = array();
+    foreach ($object as $fieldItem) {
+      $attributes[] = $this->serializer->normalize($fieldItem, $format);
+    }
+    return $attributes;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Serialization/NormalizerBase.php b/core/lib/Drupal/Core/Serialization/NormalizerBase.php
new file mode 100644
index 0000000..f256b0e
--- /dev/null
+++ b/core/lib/Drupal/Core/Serialization/NormalizerBase.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\Serialization\NormalizerBase.
+ */
+
+namespace Drupal\Core\Serialization;
+
+use Drupal\Core\Entity\EntityInterface;
+use Symfony\Component\Serializer\Exception\RuntimeException;
+use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
+use Symfony\Component\Serializer\Normalizer\SerializerAwareNormalizer;
+
+/**
+ * Base class for Normalizers.
+ */
+abstract class NormalizerBase extends SerializerAwareNormalizer implements NormalizerInterface {
+
+  /**
+   * The interface or class that this Normalizer supports.
+   *
+   * @var string
+   */
+  protected static $supportedInterfaceOrClass;
+
+  /**
+   * Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::supportsNormalization().
+   */
+  public function supportsNormalization($data, $format = NULL) {
+    return is_object($data) && ($data instanceof static::$supportedInterfaceOrClass);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Serialization/TypedDataNormalizer.php b/core/lib/Drupal/Core/Serialization/TypedDataNormalizer.php
new file mode 100644
index 0000000..0376771
--- /dev/null
+++ b/core/lib/Drupal/Core/Serialization/TypedDataNormalizer.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Serialization\TypedDataNormalizer.
+ */
+
+namespace Drupal\Core\Serialization;
+
+use Drupal\Core\Serialization\NormalizerBase;
+use Symfony\Component\Serializer\Exception\RuntimeException;
+
+/**
+ * Converts typed data objects to arrays.
+ */
+class TypedDataNormalizer extends NormalizerBase {
+
+  /**
+   * The interface or class that this Normalizer supports.
+   *
+   * @var string
+   */
+  protected static $supportedInterfaceOrClass = 'Drupal\Core\TypedData\TypedDataInterface';
+
+  /**
+   * Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize().
+   */
+  public function normalize($object, $format = NULL) {
+    return $object->getValue();
+  }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Serialization/EntitySerializationTest.php b/core/modules/system/lib/Drupal/system/Tests/Serialization/EntitySerializationTest.php
new file mode 100644
index 0000000..f34cd9d
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Serialization/EntitySerializationTest.php
@@ -0,0 +1,128 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Serialization\EntitySerializationTest.
+ */
+
+namespace Drupal\system\Tests\Serialization;
+
+use Drupal\simpletest\WebTestBase;
+use Symfony\Component\Serializer\Serializer;
+use Drupal\Core\Serialization\JsonEncoder;
+use Drupal\Core\Serialization\ComplexDataNormalizer;
+use Drupal\Core\Serialization\TypedDataNormalizer;
+
+/**
+ * Tests entity normalization and serialization of supported core formats.
+ */
+class EntitySerializationTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('entity_test');
+
+  /**
+   * The test values.
+   *
+   * @var array
+   */
+  protected $values;
+
+  /**
+   * The test entity.
+   *
+   * @var \Drupal\Core\Entity\EntityNG
+   */
+  protected $entity;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Entity serialization tests',
+      'description' => 'Tests that entities can be serialized to supported core formats.',
+      'group' => 'Serialization',
+    );
+  }
+
+  protected function setUp() {
+    parent::setUp();
+
+    // Create a test entity to serialize.
+    $this->values = array(
+      'name' => $this->randomName(),
+      'user_id' => $GLOBALS['user']->uid,
+      'field_test_text' => array(
+        'value' => $this->randomName(),
+        'format' => 'full_html',
+      ),
+    );
+    $this->entity = entity_create('entity_test', $this->values);
+    $this->entity->save();
+  }
+
+  /**
+   * Test the normalize function.
+   */
+  public function testNormalize() {
+    // Set up the serializer.
+    $encoders = array(new JsonEncoder());
+    $normalizers = array(new ComplexDataNormalizer(), new TypedDataNormalizer());
+    $serializer = new Serializer($normalizers, $encoders);
+
+    $expected = array(
+      'id' => array(
+        array('value' => 1),
+      ),
+      'revision_id' => array(
+        array('value' => 1),
+      ),
+      'uuid' => array(
+        array('value' => $this->entity->uuid()),
+      ),
+      'langcode' => array(
+        array('value' => LANGUAGE_NOT_SPECIFIED),
+      ),
+      'default_langcode' => array(NULL),
+      'name' => array(
+        array('value' => $this->values['name']),
+      ),
+      'user_id' => array(
+        array('value' => $this->values['user_id']),
+      ),
+      'field_test_text' => array(
+        array(
+          'value' => $this->values['field_test_text']['value'],
+          'format' => $this->values['field_test_text']['format'],
+        ),
+      ),
+    );
+
+    $normalized = $serializer->normalize($this->entity);
+
+    foreach (array_keys($expected) as $fieldName) {
+      $this->assertEqual($expected[$fieldName], $normalized[$fieldName], "ComplexDataNormalizer produces expected array for $fieldName.");
+    }
+    $this->assertEqual(array_keys($expected), array_keys($normalized), 'No unexpected data is added to the normalized array.');
+  }
+
+  /**
+   * Test registered Serializer's entity serialization for core's formats.
+   */
+  public function testSerialize() {
+    $serializer = drupal_container()->get('serializer');
+    // Test that Serializer responds using the ComplexDataNormalizer and
+    // JsonEncoder. The output of ComplexDataNormalizer::normalize() is tested
+    // elsewhere, so we can just assume that it works properly here.
+    $normalized = $serializer->normalize($this->entity, 'json');
+    $expected = json_encode($normalized);
+    // Test 'json'.
+    $actual = $serializer->serialize($this->entity, 'json');
+    $this->assertIdentical($actual, $expected, 'Entity serializes to JSON when json is requested.');
+    // Test 'ajax'.
+    $actual = $serializer->serialize($this->entity, 'ajax');
+    $this->assertIdentical($actual, $expected, 'Entity serializes to JSON when ajax is requested.');
+  }
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Serialization/SerializationTest.php b/core/modules/system/lib/Drupal/system/Tests/Serialization/SerializationTest.php
index cf766ec..0a69208 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Serialization/SerializationTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Serialization/SerializationTest.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Definition of Drupal\jsonld\Tests\SerializationTest.
+ * Definition of Drupal\system\Tests\Serialization\SerializationTest.
  */
 
 namespace Drupal\system\Tests\Serialization;
