diff --git a/core/modules/rest/src/Tests/NodeTest.php b/core/modules/rest/src/Tests/NodeTest.php
index 2c8de1e..d55d7ce 100644
--- a/core/modules/rest/src/Tests/NodeTest.php
+++ b/core/modules/rest/src/Tests/NodeTest.php
@@ -8,7 +8,7 @@
 namespace Drupal\rest\Tests;
 
 use Drupal\Core\Url;
-use Drupal\rest\Tests\RESTTestBase;
+use Drupal\node\Entity\Node;
 
 /**
  * Tests special cases for node entities.
@@ -43,6 +43,40 @@ protected function enableNodeConfiguration($method, $operation) {
   }
 
   /**
+   * Serializes and post a node to create it.
+   *
+   * @param array $data
+   */
+  protected function postNode($data) {
+    // Enable node creation via POST.
+    $this->enableNodeConfiguration('POST', 'create');
+    $this->enableService('entity:node', 'POST', 'json');
+
+    // Create a JSON version of a simple node with the title.
+    $serialized = $this->container->get('serializer')->serialize($data, 'json');
+
+    // Post to the REST service to create the node.
+    $this->httpRequest('/entity/node', 'POST', $serialized, 'application/json');
+  }
+
+  /**
+   * Testes the title on a newly created node.
+   *
+   * @param array $data
+   * @return \Drupal\node\Entity\Node
+   */
+  protected function assertNodeTitleMatch($data) {
+    /** @var \Drupal\node\Entity\Node $node */
+    // Load the newly created node.
+    $node = Node::load(1);
+
+    // Test that the title is the same as what we posted.
+    $this->assertEqual($node->title->value, $data['title'][0]['value']);
+
+    return $node;
+  }
+
+  /**
    * Performs various tests on nodes and their REST API.
    */
   public function testNodes() {
@@ -90,4 +124,80 @@ public function testNodes() {
     // Make sure that the UUID of the node has not changed.
     $this->assertEqual($node->get('uuid')->getValue(), $updated_node->get('uuid')->getValue(), 'UUID was not changed.');
   }
+
+  /**
+   * Test creating a node using json serialization.
+   */
+  public function testCreate() {
+    // Data to be used for serialization.
+    $data = [
+      'type' => [['target_id' => 'resttest']],
+      'title' => [['value' => $this->randomString() ]],
+    ];
+
+    $this->postNode($data);
+
+    // Make sure the response is "CREATED".
+    $this->assertResponse(201);
+
+    // Make sure the node was created and the title matches.
+    $node = $this->assertNodeTitleMatch($data);
+
+    // Make sure the request returned a redirect header to view the node.
+    $this->assertHeader('Location', $node->url('canonical', ['absolute' => TRUE]));
+  }
+
+  /**
+   * Test bundle normalization when posting bundle as a simple string.
+   */
+  public function testBundleNormalization() {
+    // Data to be used for serialization.
+    $data = [
+      'type' => 'resttest',
+      'title' => [['value' => $this->randomString() ]],
+    ];
+
+    // testing
+    $this->postNode($data);
+
+    // Make sure the response is "CREATED".
+    $this->assertResponse(201);
+
+    // Make sure the node was created and the title matches.
+    $this->assertNodeTitleMatch($data);
+  }
+
+  /**
+   * Test bundle normalization when posting using a simple string.
+   */
+  public function testInvalidBundle() {
+    // Data to be used for serialization.
+    $data = [
+      'type' => 'bad_bundle_name',
+      'title' => [['value' => $this->randomString() ]],
+    ];
+
+    $this->postNode($data);
+
+    // Make sure the response is "Bad Request".
+    $this->assertResponse(400);
+    $this->assertResponseBody('{"error":"\"bad_bundle_name\" is not a valid bundle type for denormalization."}');
+  }
+
+  /**
+   * Test when the bundle is missing.
+   */
+  public function testMissingBundle() {
+    // Data to be used for serialization.
+    $data = [
+      'title' => [['value' => $this->randomString() ]],
+    ];
+
+    // testing
+    $this->postNode($data);
+
+    // Make sure the response is "Bad Request".
+    $this->assertResponse(400);
+    $this->assertResponseBody('{"error":"\"\" is not a valid bundle type for denormalization."}');
+  }
 }
diff --git a/core/modules/rest/src/Tests/RESTTestBase.php b/core/modules/rest/src/Tests/RESTTestBase.php
index 46003af..b75a02a 100644
--- a/core/modules/rest/src/Tests/RESTTestBase.php
+++ b/core/modules/rest/src/Tests/RESTTestBase.php
@@ -7,11 +7,9 @@
 
 namespace Drupal\rest\Tests;
 
-use Drupal\Core\Session\AccountInterface;
-use Drupal\Core\Url;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\node\NodeInterface;
 use Drupal\simpletest\WebTestBase;
-use Drupal\user\UserInterface;
 
 /**
  * Test helper class that provides a REST client method to send HTTP requests.
@@ -46,6 +44,14 @@
    */
   protected $defaultAuth;
 
+
+  /**
+   * The raw response body from http request operations.
+   *
+   * @var array
+   */
+  protected $responseBody;
+
   /**
    * Modules to install.
    *
@@ -153,7 +159,7 @@ protected function httpRequest($url, $method, $body = NULL, $mime_type = NULL) {
         break;
     }
 
-    $response = $this->curlExec($curl_options);
+    $this->responseBody = $this->curlExec($curl_options);
 
     // Ensure that any changes to variables in the other thread are picked up.
     $this->refreshVariables();
@@ -163,9 +169,9 @@ protected function httpRequest($url, $method, $body = NULL, $mime_type = NULL) {
     $this->verbose($method . ' request to: ' . $url .
       '<hr />Code: ' . curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE) .
       '<hr />Response headers: ' . nl2br(print_r($headers, TRUE)) .
-      '<hr />Response body: ' . $response);
+      '<hr />Response body: ' . $this->responseBody);
 
-    return $response;
+    return $this->responseBody;
   }
 
   /**
@@ -361,4 +367,27 @@ protected function removeNodeFieldsForNonAdminUsers(NodeInterface $node) {
     return $node;
   }
 
+  /**
+   * Check to see if the http request response body is identical to the expected
+   * value.
+   *
+   * @param $expected
+   *   The first value to check.
+   * @param $message
+   *   (optional) A message to display with the assertion. Do not translate
+   *   messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
+   *   variables in the message text, not t(). If left blank, a default message
+   *   will be displayed.
+   * @param $group
+   *   (optional) The group this message is in, which is displayed in a column
+   *   in test output. Use 'Debug' to indicate this is debugging output. Do not
+   *   translate this string. Defaults to 'Other'; most tests do not override
+   *   this default.
+   *
+   * @return
+   *   TRUE if the assertion succeeded, FALSE otherwise.
+   */
+  protected function assertResponseBody($expected, $message = '', $group = 'REST Response') {
+    return $this->assertIdentical($expected, $this->responseBody, $message ? $message : SafeMarkup::format('Response body @expected (expected) is equal to @response (actual).', array('@expected' => var_export($expected, TRUE), '@response' => var_export($this->responseBody, TRUE))), $group);
+  }
 }
diff --git a/core/modules/serialization/src/Normalizer/EntityNormalizer.php b/core/modules/serialization/src/Normalizer/EntityNormalizer.php
index 5e81b5a..35aa0b0 100644
--- a/core/modules/serialization/src/Normalizer/EntityNormalizer.php
+++ b/core/modules/serialization/src/Normalizer/EntityNormalizer.php
@@ -43,22 +43,55 @@ public function __construct(EntityManagerInterface $entity_manager) {
   /**
    * {@inheritdoc}
    */
-  public function denormalize($data, $class, $format = NULL, array $context = array()) {
-    if (empty($context['entity_type'])) {
-      throw new UnexpectedValueException('Entity type parameter must be included in context.');
+  public function denormalize($data, $class, $format = NULL, array $context = []) {
+    // Get the entity type ID letting the context definition override the $class.
+    $entity_type_id = !empty($context['entity_type']) ? $context['entity_type'] : $this->entityManager->getEntityTypeFromClass($class);
+
+    /** @var \Drupal\Core\Entity\EntityTypeInterface $entity_type_definition */
+    // Get the entity type definition.
+    $entity_type_definition = $this->entityManager->getDefinition($entity_type_id, FALSE);
+
+    // Don't try to create an entity without an entity type id.
+    if (!$entity_type_definition) {
+      throw new UnexpectedValueException('A valid entity type is required for denormalization.');
     }
 
-    $entity_type = $this->entityManager->getDefinition($context['entity_type']);
+    // The bundle property will be required to denormalize a bundleable entity.
+    if ($entity_type_definition->hasKey('bundle')) {
+      $bundle_key = $entity_type_definition->getKey('bundle');
+      // Get the base field definitions for this entity type.
+      $base_field_definitions = $this->entityManager->getBaseFieldDefinitions($entity_type_id);
+
+      // Set a default key ID.
+      $key_id = 'value';
+      // Get the ID key from the base field definition for the bundle key.
+      if (isset($base_field_definitions[$bundle_key])) {
+        $key_id = $base_field_definitions[$bundle_key]->getFieldStorageDefinition()->getMainPropertyName();
+      }
+
+      // Normalize the bundle if it is not explicitly set.
+      $data[$bundle_key] = isset($data[$bundle_key][0][$key_id])
+        ? $data[$bundle_key][0][$key_id]
+        : (isset($data[$bundle_key]) ? $data[$bundle_key] : NULL);
 
-    // The bundle property behaves differently from other entity properties.
-    // i.e. the nested structure with a 'value' key does not work.
-    if ($entity_type->hasKey('bundle')) {
-      $bundle_key = $entity_type->getKey('bundle');
-      $type = $data[$bundle_key][0]['value'];
-      $data[$bundle_key] = $type;
+      // Get the bundle entity type from the entity type definition.
+      $bundle_entity_type = $entity_type_definition->getBundleEntityType();
+      // @todo http://drupal.org/node/2346857 default to NULL.
+      $bundle_types = ($bundle_entity_type !== 'bundle') ? $this->entityManager->getStorage($bundle_entity_type)->getQuery()->execute() : [];
+
+      // Make sure the submitted bundle is a valid bundle for the entity type.
+      if (!is_string($data[$bundle_key]) || ($bundle_types && !in_array($data[$bundle_key], $bundle_types))) {
+        throw new UnexpectedValueException(sprintf('"%s" is not a valid bundle type for denormalization.', $data[$bundle_key]));
+      }
     }
 
-    return $this->entityManager->getStorage($context['entity_type'])->create($data);
-  }
+    // Create the entity from data.
+    $entity = $this->entityManager->getStorage($entity_type_id)->create($data);
 
+    // Pass the names of the fields whose values can be merged.
+    // @todo https://www.drupal.org/node/2456257 remove this.
+    $entity->_restSubmittedFields = array_keys($data);
+
+    return $entity;
+  }
 }
diff --git a/core/modules/serialization/tests/src/Unit/Normalizer/EntityNormalizerTest.php b/core/modules/serialization/tests/src/Unit/Normalizer/EntityNormalizerTest.php
index 652c650..0cd5983 100644
--- a/core/modules/serialization/tests/src/Unit/Normalizer/EntityNormalizerTest.php
+++ b/core/modules/serialization/tests/src/Unit/Normalizer/EntityNormalizerTest.php
@@ -99,14 +99,14 @@ public function testDenormalizeWithNoEntityType() {
    *
    * @covers ::denormalize
    */
-  public function testDenormalizeWithBundle() {
-    $test_data = array(
+  public function testDenormalizeWithValidBundle() {
+    $test_data = [
       'key_1' => 'value_1',
       'key_2' => 'value_2',
-      'test_type' => array(
-        array('value' => 'test_bundle'),
-      ),
-    );
+      'test_type' => [
+        ['name' => 'test_bundle'],
+      ],
+    ];
 
     $entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
     $entity_type->expects($this->once())
@@ -117,11 +117,47 @@ public function testDenormalizeWithBundle() {
       ->method('getKey')
       ->with('bundle')
       ->will($this->returnValue('test_type'));
+    $entity_type->expects($this->once())
+      ->method('getBundleEntityType')
+      ->will($this->returnValue('test_bundle'));
 
-    $this->entityManager->expects($this->once())
+    $entity_type_storage_definition = $this->getmock('Drupal\Core\Field\FieldStorageDefinitionInterface');
+    $entity_type_storage_definition->expects($this->once())
+      ->method('getMainPropertyName')
+      ->will($this->returnValue('name'));
+
+    $entity_type_definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
+    $entity_type_definition->expects($this->once())
+      ->method('getFieldStorageDefinition')
+      ->will($this->returnValue($entity_type_storage_definition));
+
+    $base_definitions = [
+      'test_type' => $entity_type_definition,
+    ];
+
+    $this->entityManager->expects($this->at(0))
       ->method('getDefinition')
       ->with('test')
       ->will($this->returnValue($entity_type));
+    $this->entityManager->expects($this->at(1))
+      ->method('getBaseFieldDefinitions')
+      ->with('test')
+      ->will($this->returnValue($base_definitions));
+
+    $entity_query_mock = $this->getMock('Drupal\Core\Entity\Query\QueryInterface');
+    $entity_query_mock->expects($this->once())
+      ->method('execute')
+      ->will($this->returnValue(['test_bundle' => 'test_bundle']));
+
+    $entity_type_storage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
+    $entity_type_storage->expects($this->once())
+      ->method('getQuery')
+      ->will($this->returnValue($entity_query_mock));
+
+    $this->entityManager->expects($this->at(2))
+      ->method('getStorage')
+      ->with('test_bundle')
+      ->will($this->returnValue($entity_type_storage));
 
     // The expected test data should have a modified test_type property.
     $expected_test_data = array(
@@ -136,12 +172,83 @@ public function testDenormalizeWithBundle() {
       ->with($expected_test_data)
       ->will($this->returnValue($this->getMock('Drupal\Core\Entity\EntityInterface')));
 
-    $this->entityManager->expects($this->once())
+    $this->entityManager->expects($this->at(3))
       ->method('getStorage')
       ->with('test')
       ->will($this->returnValue($storage));
 
-    $this->assertNotNull($this->entityNormalizer->denormalize($test_data, 'Drupal\Core\Entity\ContentEntityBase', NULL, array('entity_type' => 'test')));
+    $this->assertNotNull($this->entityNormalizer->denormalize($test_data, 'Drupal\Core\Entity\ContentEntityBase', NULL, ['entity_type' => 'test']));
+  }
+
+  /**
+   * Tests the denormalize method with a bundle property.
+   *
+   * @expectedException \Symfony\Component\Serializer\Exception\UnexpectedValueException
+   *
+   * @covers ::denormalize
+   */
+  public function testDenormalizeWithInvalidBundle() {
+    $test_data = [
+      'key_1' => 'value_1',
+      'key_2' => 'value_2',
+      'test_type' => [
+        ['name' => 'test_bundle'],
+      ],
+    ];
+
+    $entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
+    $entity_type->expects($this->once())
+      ->method('hasKey')
+      ->with('bundle')
+      ->will($this->returnValue(TRUE));
+    $entity_type->expects($this->once())
+      ->method('getKey')
+      ->with('bundle')
+      ->will($this->returnValue('test_type'));
+    $entity_type->expects($this->once())
+      ->method('getBundleEntityType')
+      ->will($this->returnValue('test_bundle'));
+
+    $entity_type_storage_definition = $this->getmock('Drupal\Core\Field\FieldStorageDefinitionInterface');
+    $entity_type_storage_definition->expects($this->once())
+      ->method('getMainPropertyName')
+      ->will($this->returnValue('name'));
+
+    $entity_type_definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
+    $entity_type_definition->expects($this->once())
+      ->method('getFieldStorageDefinition')
+      ->will($this->returnValue($entity_type_storage_definition));
+
+    $base_definitions = [
+      'test_type' => $entity_type_definition,
+    ];
+
+    $this->entityManager->expects($this->at(0))
+      ->method('getDefinition')
+      ->with('test')
+      ->will($this->returnValue($entity_type));
+    $this->entityManager->expects($this->at(1))
+      ->method('getBaseFieldDefinitions')
+      ->with('test')
+      ->will($this->returnValue($base_definitions));
+
+    $entity_query_mock = $this->getMock('Drupal\Core\Entity\Query\QueryInterface');
+    $entity_query_mock->expects($this->once())
+      ->method('execute')
+      ->will($this->returnValue(['test_bundle_other' => 'test_bundle_other']));
+
+    $entity_type_storage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
+    $entity_type_storage->expects($this->once())
+      ->method('getQuery')
+      ->will($this->returnValue($entity_query_mock));
+
+    $this->entityManager->expects($this->at(2))
+      ->method('getStorage')
+      ->with('test_bundle')
+      ->will($this->returnValue($entity_type_storage));
+
+
+    $this->entityNormalizer->denormalize($test_data, 'Drupal\Core\Entity\ContentEntityBase', NULL, ['entity_type' => 'test']);
   }
 
   /**
@@ -179,6 +286,9 @@ public function testDenormalizeWithNoBundle() {
       ->with('test')
       ->will($this->returnValue($storage));
 
+    $this->entityManager->expects($this->never())
+      ->method('getBaseFieldDefinitions');
+
     $this->assertNotNull($this->entityNormalizer->denormalize($test_data, 'Drupal\Core\Entity\ContentEntityBase', NULL, array('entity_type' => 'test')));
   }
 
