diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
index 826193b..0f46763 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
@@ -790,20 +790,23 @@ public function __set($name, $value) {
    */
   public function __isset($name) {
     if ($this->hasField($name)) {
-      return $this->get($name)->getValue() !== NULL;
+      return $this->get($name)->isEmpty();
     }
+    // For non-field properties, check the internal values.
     else {
       return isset($this->values[$name]);
     }
   }
 
   /**
-   * Implements the magic method for unset.
+   * Implements the magic method for unset().
    */
   public function __unset($name) {
+    // Unsetting a field means emptying it.
     if ($this->hasField($name)) {
-      $this->get($name)->setValue(NULL);
+      $this->get($name)->setValue(array());
     }
+    // For non-field properties, unset the internal value.
     else {
       unset($this->values[$name]);
     }
diff --git a/core/lib/Drupal/Core/Field/FieldItemList.php b/core/lib/Drupal/Core/Field/FieldItemList.php
index 27305dc..e58b5a6 100644
--- a/core/lib/Drupal/Core/Field/FieldItemList.php
+++ b/core/lib/Drupal/Core/Field/FieldItemList.php
@@ -125,7 +125,7 @@ public function getValue($include_computed = FALSE) {
    */
   public function setValue($values, $notify = TRUE) {
     if (!isset($values) || $values === array()) {
-      $this->list = $values;
+      $this->list = array();
     }
     else {
       // Support passing in only the value of the first item.
diff --git a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php
index 8ae74de..4a453b0 100644
--- a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php
+++ b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php
@@ -152,15 +152,6 @@ public function denormalize($data, $class, $format = NULL, array $context = arra
 
     $entity = $this->entityManager->getStorage($typed_data_ids['entity_type'])->create($values);
 
-    // Special handling for PATCH: destroy all possible default values that
-    // might have been set on entity creation. We want an "empty" entity that
-    // will only get filled with fields from the data array.
-    if (isset($context['request_method']) && $context['request_method'] == 'patch') {
-      foreach ($entity as $field_name => $field) {
-        $entity->set($field_name, NULL);
-      }
-    }
-
     // Remove links from data array.
     unset($data['_links']);
     // Get embedded resources and remove from data array.
@@ -179,6 +170,12 @@ public function denormalize($data, $class, $format = NULL, array $context = arra
       }
     }
 
+    // Special handling for PATCH: pass the names of the fields whose values
+    // should be merged.
+    if (isset($context['request_method']) && $context['request_method'] == 'patch') {
+      $entity->_restPatchFields = array_keys($data);
+    }
+
     // Iterate through remaining items in data array. These should all
     // correspond to fields.
     foreach ($data as $field_name => $field_data) {
diff --git a/core/modules/hal/src/Tests/DenormalizeTest.php b/core/modules/hal/src/Tests/DenormalizeTest.php
index 8b5711c..6931d1c 100644
--- a/core/modules/hal/src/Tests/DenormalizeTest.php
+++ b/core/modules/hal/src/Tests/DenormalizeTest.php
@@ -176,7 +176,7 @@ public function testBasicFieldDenormalization() {
   }
 
   /**
-   * Verifies that only specified properties get populated in the PATCH context.
+   * Verifies that the denormalized entity is correct in the PATCH context.
    */
   public function testPatchDenormailzation() {
     $data = array(
@@ -195,15 +195,7 @@ public function testPatchDenormailzation() {
     $denormalized = $this->serializer->denormalize($data, $this->entityClass, $this->format, array('request_method' => 'patch'));
     // Check that the one field got populated as expected.
     $this->assertEqual($data['field_test_text'], $denormalized->get('field_test_text')->getValue());
-    // Unset that field so that now all fields are NULL.
-    $denormalized->set('field_test_text', NULL);
-    // Assert that all fields are NULL and not set to default values. Example:
-    // the UUID field is NULL and not initialized as usual.
-    foreach ($denormalized as $field_name => $field) {
-      // The 'langcode' field always has a value.
-      if ($field_name != 'langcode') {
-        $this->assertFalse(isset($denormalized->$field_name), "$field_name is not set.");
-      }
-    }
+    // Check the custom property that contains the list of fields to merge.
+    $this->assertEqual($denormalized->_restPatchFields, ['field_test_text']);
   }
 }
diff --git a/core/modules/quickedit/src/Form/QuickEditFieldForm.php b/core/modules/quickedit/src/Form/QuickEditFieldForm.php
index a1516fe..6d9d2c3 100644
--- a/core/modules/quickedit/src/Form/QuickEditFieldForm.php
+++ b/core/modules/quickedit/src/Form/QuickEditFieldForm.php
@@ -187,7 +187,7 @@ protected function buildEntity(array $form, FormStateInterface $form_state) {
 
     // @todo Refine automated log messages and abstract them to all entity
     //   types: http://drupal.org/node/1678002.
-    if ($entity->getEntityTypeId() == 'node' && $entity->isNewRevision() && !isset($entity->revision_log)) {
+    if ($entity->getEntityTypeId() == 'node' && $entity->isNewRevision() && $entity->revision_log->isEmpty()) {
       $entity->revision_log = t('Updated the %field-name field through in-place editing.', array('%field-name' => $entity->get($field_name)->getFieldDefinition()->getLabel()));
     }
 
diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
index c58ff1a..0c593ae 100644
--- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
@@ -11,7 +11,6 @@
 use Drupal\Core\Entity\EntityStorageException;
 use Drupal\rest\Plugin\ResourceBase;
 use Drupal\rest\ResourceResponse;
-use Drupal\Component\Utility\String;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
 use Symfony\Component\HttpKernel\Exception\HttpException;
@@ -134,22 +133,21 @@ public function patch(EntityInterface $original_entity, EntityInterface $entity
     }
 
     // Overwrite the received properties.
-    foreach ($entity as $field_name => $field) {
-      if (isset($entity->{$field_name})) {
-        // It is not possible to set the language to NULL as it is automatically
-        // re-initialized. As it must not be empty, skip it if it is.
-        // @todo: Use the langcode entity key when available. See
-        //   https://drupal.org/node/2143729.
-        if ($field_name == 'langcode' && $field->isEmpty()) {
-          continue;
-        }
-        if ($field->isEmpty() && !$original_entity->get($field_name)->access('delete')) {
-          throw new AccessDeniedHttpException(String::format('Access denied on deleting field ', array('@field' => $field_name)));
-        }
-        $original_entity->set($field_name, $field->getValue());
-        if (!$original_entity->get($field_name)->access('update')) {
-          throw new AccessDeniedHttpException(String::format('Access denied on updating field ', array('@field' => $field_name)));
-        }
+    foreach ($entity->_restPatchFields as $field_name) {
+      $field = $entity->get($field_name);
+      // It is not possible to set the language to NULL as it is automatically
+      // re-initialized. As it must not be empty, skip it if it is.
+      // @todo: Use the langcode entity key when available. See
+      //   https://drupal.org/node/2143729.
+      if ($field_name == 'langcode' && $field->isEmpty()) {
+        continue;
+      }
+      if ($field->isEmpty() && !$original_entity->get($field_name)->access('delete')) {
+        throw new AccessDeniedHttpException(String::format('Access denied on deleting field @field.', array('@field' => $field_name)));
+      }
+      $original_entity->set($field_name, $field->getValue());
+      if (!$original_entity->get($field_name)->access('update')) {
+        throw new AccessDeniedHttpException(String::format('Access denied on updating field @field.', array('@field' => $field_name)));
       }
     }
 
diff --git a/core/modules/system/src/Tests/Entity/EntityFieldTest.php b/core/modules/system/src/Tests/Entity/EntityFieldTest.php
index 41dbb44..6748b3c 100644
--- a/core/modules/system/src/Tests/Entity/EntityFieldTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityFieldTest.php
@@ -141,7 +141,7 @@ protected function doTestReadWrite($entity_type) {
     $this->assertEqual($new_user->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type)));
     $this->assertEqual($new_user->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated username value can be read.', array('%entity_type' => $entity_type)));
 
-    // Try unsetting a field.
+    // Try unsetting a field property.
     $entity->name->value = NULL;
     $entity->user_id->target_id = NULL;
     $this->assertNull($entity->name->value, format_string('%entity_type: Name field is not set.', array('%entity_type' => $entity_type)));
@@ -171,25 +171,27 @@ protected function doTestReadWrite($entity_type) {
     $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type)));
     $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type)));
 
-    $entity->name = array();
-    $this->assertTrue(isset($entity->name), 'Name field is set.');
-    $this->assertFalse(isset($entity->name[0]), 'Name field item is not set.');
-    $this->assertFalse(isset($entity->name[0]->value), 'First name item value is not set.');
-    $this->assertFalse(isset($entity->name->value), 'Name value is not set.');
-
-    $entity->name = NULL;
-    $this->assertFalse(isset($entity->name), 'Name field is not set.');
-    $this->assertFalse(isset($entity->name[0]), 'Name field item is not set.');
-    $this->assertFalse(isset($entity->name[0]->value), 'First name item value is not set.');
-    $this->assertFalse(isset($entity->name->value), 'Name value is not set.');
-
-    $entity->name->value = 'a value';
-    $this->assertTrue(isset($entity->name->value), format_string('%entity_type: Name is set.', array('%entity_type' => $entity_type)));
-    unset($entity->name);
-    $this->assertFalse(isset($entity->name), format_string('%entity_type: Name field is not set.', array('%entity_type' => $entity_type)));
-    $this->assertFalse(isset($entity->name[0]), format_string('%entity_type: Name field item is not set.', array('%entity_type' => $entity_type)));
-    $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type)));
-    $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type)));
+    // Test emptying a field by assigning an empty value. NULL and array()
+    // behave the same.
+    foreach ([NULL, array(), 'unset'] as $empty) {
+      // Make sure a value is present
+      $entity->name->value = 'a value';
+      $this->assertTrue(isset($entity->name->value), format_string('%entity_type: Name is set.', array('%entity_type' => $entity_type)));
+      // Now, empty the field.
+      if ($empty === 'unset') {
+        unset($entity->name);
+      }
+      else {
+        $entity->name = $empty;
+      }
+      $this->assertTrue(isset($entity->name), format_string('%entity_type: Name field is set.', array('%entity_type' => $entity_type)));
+      $this->assertTrue($entity->name->isEmpty(), format_string('%entity_type: Name field is set.', array('%entity_type' => $entity_type)));
+      $this->assertIdentical(count($entity->name), 0, format_string('%entity_type: Name field contains no items.', array('%entity_type' => $entity_type)));
+      $this->assertIdentical($entity->name->getValue(), array(), format_string('%entity_type: Name field value is an empty array.', array('%entity_type' => $entity_type)));
+      $this->assertFalse(isset($entity->name[0]), format_string('%entity_type: Name field item is not set.', array('%entity_type' => $entity_type)));
+      $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: First name item value is not set.', array('%entity_type' => $entity_type)));
+      $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name value is not set.', array('%entity_type' => $entity_type)));
+    }
 
     // Access the language field.
     $this->assertEqual($langcode, $entity->langcode->value, format_string('%entity_type: Language code can be read.', array('%entity_type' => $entity_type)));
@@ -273,18 +275,6 @@ protected function doTestReadWrite($entity_type) {
     $this->assertEqual($entity->name[0]->value, 'foo', format_string('%entity_type: The items were renumbered.', array('%entity_type' => $entity_type)));
     $this->assertEqual($entity->name[0]->getName(), 0, format_string('%entity_type: The deltas were updated in the items.', array('%entity_type' => $entity_type)));
 
-    // Test removing all list items by assigning an empty array.
-    $entity->name = array();
-    $this->assertIdentical(count($entity->name), 0, format_string('%entity_type: Name field contains no items.', array('%entity_type' => $entity_type)));
-    $this->assertIdentical($entity->name->getValue(), array(), format_string('%entity_type: Name field value is an empty array.', array('%entity_type' => $entity_type)));
-
-    $entity->name->value = 'foo';
-    $this->assertEqual($entity->name->value, 'foo', format_string('%entity_type: Name field set.', array('%entity_type' => $entity_type)));
-    // Test removing all list items by setting it to NULL.
-    $entity->name = NULL;
-    $this->assertIdentical(count($entity->name), 0, format_string('%entity_type: Name field contains no items.', array('%entity_type' => $entity_type)));
-    $this->assertNull($entity->name->getValue(), format_string('%entity_type: Name field value is an empty array.', array('%entity_type' => $entity_type)));
-
     // Test get and set field values.
     $entity->name = 'foo';
     $this->assertEqual($entity->name[0]->toArray(), array('value' => 'foo'), format_string('%entity_type: Field value has been retrieved via toArray()', array('%entity_type' => $entity_type)));
