diff --git a/core/modules/hal/src/Normalizer/EntityReferenceEmbeddingTrait.php b/core/modules/hal/src/Normalizer/EntityReferenceEmbeddingTrait.php
new file mode 100644
index 0000000..2424f43
--- /dev/null
+++ b/core/modules/hal/src/Normalizer/EntityReferenceEmbeddingTrait.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace Drupal\hal\Normalizer;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\rest\LinkManager\LinkManagerInterface;
+use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
+
+/**
+ * Defines a trait for embedding one entity into another one.
+ */
+trait EntityReferenceEmbeddingTrait {
+
+  /**
+   * Embeds an entity into normalized data.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $host_entity
+   *   Entity hosting the entity reference.
+   * @param \Drupal\Core\Entity\EntityInterface $target_entity
+   *   Entity being reference.
+   * @param string $field_name
+   *   Field name holding the reference.
+   * @param \Symfony\Component\Serializer\Normalizer\NormalizerInterface $normalizer
+   *   Normalizer service.
+   * @param \Drupal\rest\LinkManager\LinkManagerInterface $link_manager
+   *   Rest link manager service.
+   * @param string $format
+   *   Normalizer format.
+   * @param array $context
+   *   Normalizer context.
+   *
+   * @return array
+   *   Embedded data to be added to normalized result.
+   */
+  protected function embedTargetEntity(EntityInterface $host_entity, EntityInterface $target_entity, $field_name, NormalizerInterface $normalizer, LinkManagerInterface $link_manager, $format, array $context = []) {
+    // If the parent entity passed in a langcode, unset it before normalizing
+    // the target entity. Otherwise, untranslatable fields of the target entity
+    // will include the langcode.
+    $langcode = isset($context['langcode']) ? $context['langcode'] : NULL;
+    unset($context['langcode']);
+    $context['included_fields'] = array('uuid');
+
+    // Normalize the target entity.
+    $embedded = $normalizer->normalize($target_entity, $format, $context);
+    $link = $embedded['_links']['self'];
+    // If the field is translatable, add the langcode to the link relation
+    // object. This does not indicate the language of the target entity.
+    if ($langcode) {
+      $embedded['lang'] = $link['lang'] = $langcode;
+    }
+
+    // The returned structure will be recursively merged into the normalized
+    // entity so that the items are properly added to the _links and _embedded
+    // objects.
+    $field_uri = $link_manager->getRelationUri($host_entity->getEntityTypeId(), $host_entity->bundle(), $field_name, $context);
+    return array(
+      '_links' => array(
+        $field_uri => array($link),
+      ),
+      '_embedded' => array(
+        $field_uri => array($embedded),
+      ),
+    );
+  }
+
+}
diff --git a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php
index 6c73256..3fadcf3 100644
--- a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php
+++ b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php
@@ -12,6 +12,8 @@
  */
 class EntityReferenceItemNormalizer extends FieldItemNormalizer implements UuidReferenceInterface {
 
+  use EntityReferenceEmbeddingTrait;
+
   /**
    * The interface or class that this Normalizer supports.
    *
@@ -52,43 +54,14 @@ public function __construct(LinkManagerInterface $link_manager, EntityResolverIn
   public function normalize($field_item, $format = NULL, array $context = array()) {
     /** @var $field_item \Drupal\Core\Field\FieldItemInterface */
     $target_entity = $field_item->get('entity')->getValue();
-
     // If this is not a content entity, let the parent implementation handle it,
     // only content entities are supported as embedded resources.
     if (!($target_entity instanceof FieldableEntityInterface)) {
       return parent::normalize($field_item, $format, $context);
     }
-
-    // If the parent entity passed in a langcode, unset it before normalizing
-    // the target entity. Otherwise, untranslatable fields of the target entity
-    // will include the langcode.
-    $langcode = isset($context['langcode']) ? $context['langcode'] : NULL;
-    unset($context['langcode']);
-    $context['included_fields'] = array('uuid');
-
-    // Normalize the target entity.
-    $embedded = $this->serializer->normalize($target_entity, $format, $context);
-    $link = $embedded['_links']['self'];
-    // If the field is translatable, add the langcode to the link relation
-    // object. This does not indicate the language of the target entity.
-    if ($langcode) {
-      $embedded['lang'] = $link['lang'] = $langcode;
-    }
-
-    // The returned structure will be recursively merged into the normalized
-    // entity so that the items are properly added to the _links and _embedded
-    // objects.
     $field_name = $field_item->getParent()->getName();
     $entity = $field_item->getEntity();
-    $field_uri = $this->linkManager->getRelationUri($entity->getEntityTypeId(), $entity->bundle(), $field_name, $context);
-    return array(
-      '_links' => array(
-        $field_uri => array($link),
-      ),
-      '_embedded' => array(
-        $field_uri => array($embedded),
-      ),
-    );
+    return $this->embedTargetEntity($entity, $target_entity, $field_name, $this->serializer, $this->linkManager, $format, $context);
   }
 
   /**
diff --git a/core/modules/link/src/LinkServiceProvider.php b/core/modules/link/src/LinkServiceProvider.php
new file mode 100644
index 0000000..7c2455a
--- /dev/null
+++ b/core/modules/link/src/LinkServiceProvider.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Drupal\link;
+
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\DependencyInjection\ServiceProviderInterface;
+use Drupal\link\Normalizer\HalLinkItemNormalizer;
+use Drupal\link\Normalizer\LinkItemNormalizer;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Creates a service modifier to add a link item normalizer.
+ */
+class LinkServiceProvider implements ServiceProviderInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function register(ContainerBuilder $container) {
+    $modules = $container->getParameter('container.modules');
+    if (isset($modules['hal'])) {
+      // Add a hal normalizer service for LinkItem fields.
+      $service_definition = new Definition(HalLinkItemNormalizer::class, [
+        new Reference('entity_type.manager'),
+        new Reference('rest.link_manager'),
+        new Reference('entity.repository'),
+      ]);
+      // The priority must be higher than that of
+      // serializer.normalizer.field_item.hal in hal.services.yml.
+      $service_definition->addTag('normalizer', array('priority' => 40));
+      $container->setDefinition('serializer.normalizer.link_item.hal', $service_definition);
+    }
+    if (isset($modules['serialization'])) {
+      // Add a generic normalizer service for LinkItem fields.
+      $service_definition = new Definition(LinkItemNormalizer::class, [
+        new Reference('entity_type.manager'),
+      ]);
+      // The priority must be higher than that of
+      // serializer.normalizer.complex_data in serialization.services.yml.
+      $service_definition->addTag('normalizer', array('priority' => 10));
+      $container->setDefinition('serializer.normalizer.link_item', $service_definition);
+    }
+  }
+
+}
diff --git a/core/modules/link/src/Normalizer/HalLinkItemNormalizer.php b/core/modules/link/src/Normalizer/HalLinkItemNormalizer.php
new file mode 100644
index 0000000..b4c9a8c
--- /dev/null
+++ b/core/modules/link/src/Normalizer/HalLinkItemNormalizer.php
@@ -0,0 +1,115 @@
+<?php
+
+namespace Drupal\link\Normalizer;
+
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Entity\EntityRepositoryInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\hal\Normalizer\EntityReferenceEmbeddingTrait;
+use Drupal\hal\Normalizer\FieldItemNormalizer;
+use Drupal\link\Plugin\Field\FieldType\LinkItem;
+use Drupal\rest\LinkManager\LinkManagerInterface;
+
+/**
+ * A normalizer to handle LinkItem data types.
+ */
+class HalLinkItemNormalizer extends FieldItemNormalizer {
+
+  use EntityReferenceEmbeddingTrait;
+
+  /**
+   * Entity type manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $supportedInterfaceOrClass = LinkItem::class;
+
+  /**
+   * Link manager service.
+   *
+   * @var \Drupal\rest\LinkManager\LinkManagerInterface
+   */
+  protected $linkManager;
+
+  /**
+   * Entity repository service.
+   *
+   * @var \Drupal\Core\Entity\EntityRepositoryInterface
+   */
+  protected $entityRepository;
+
+  /**
+   * Constructs a new LinkItemNormalizer object.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   Entity type manager service.
+   * @param \Drupal\rest\LinkManager\LinkManagerInterface $link_manager
+   *   Link manager service.
+   * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
+   *   Entity repository service.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, LinkManagerInterface $link_manager, EntityRepositoryInterface $entity_repository) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->linkManager = $link_manager;
+    $this->entityRepository = $entity_repository;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function normalize($field_item, $format = NULL, array $context = array()) {
+    /** @var \Drupal\link\Plugin\Field\FieldType\LinkItem $field_item */
+    $field_name = $field_item->getParent()->getName();
+    $entity = $field_item->getEntity();
+    $normalized = parent::normalize($field_item, $format, $context);
+    if (isset($normalized[$field_name])) {
+      foreach ($normalized[$field_name] as $delta => $link) {
+        $url = parse_url($link['uri']);
+        if (!isset($url['scheme']) || $url['scheme'] !== 'entity') {
+          continue;
+        }
+        $path = isset($url['path']) ? $url['path'] : '';
+        list($target_entity_type_id, $target_entity_id) = explode('/', $path);
+        if ($this->entityTypeManager->hasDefinition($target_entity_type_id)) {
+          if ($target_entity = $this->entityTypeManager->getStorage($target_entity_type_id)->load($target_entity_id)) {
+            // We embed using the {field_name}_target as the URI to prevent the
+            // additional metadata like enabled, expanded etc from being lost.
+            // If we use the same field name, without the _target suffix, the
+            // \Drupal\hal\Normalizer\ContentEntityNormalizer assumes we can
+            // construct the field values from the embedded entity, which we
+            // cannot.
+            $normalized = NestedArray::mergeDeep($normalized, $this->embedTargetEntity($entity, $target_entity, $field_name . '_target', $this->serializer, $this->linkManager, $format, $context));
+            $normalized[$field_name][$delta]['target_uuid'] = $target_entity->uuid();
+          }
+        }
+      }
+    }
+
+    return $normalized;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function denormalize($link, $class, $format = NULL, array $context = array()) {
+    $url = parse_url($link['uri']);
+    if (isset($url['scheme']) && $url['scheme'] === 'entity') {
+
+      $path = isset($url['path']) ? $url['path'] : '';
+      list($target_entity_type_id, $target_entity_id) = explode('/', $path);
+      if (isset($link['target_uuid'])) {
+        if ($entity = $this->entityRepository->loadEntityByUuid($target_entity_type_id, $link['target_uuid'])) {
+          $link['uri'] = 'entity:' . $target_entity_type_id . '/' . $entity->id();
+        }
+      }
+    }
+    $field_item = parent::denormalize($link, $class, $format, $context);
+    return $field_item;
+  }
+
+}
diff --git a/core/modules/link/src/Normalizer/LinkItemNormalizer.php b/core/modules/link/src/Normalizer/LinkItemNormalizer.php
new file mode 100644
index 0000000..d816d5a
--- /dev/null
+++ b/core/modules/link/src/Normalizer/LinkItemNormalizer.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace Drupal\link\Normalizer;
+
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\link\Plugin\Field\FieldType\LinkItem;
+use Drupal\serialization\Normalizer\ComplexDataNormalizer;
+
+/**
+ * Defines a generic normalizer for LinkItem data types.
+ */
+class LinkItemNormalizer extends ComplexDataNormalizer {
+
+  /**
+   * Entity type manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $supportedInterfaceOrClass = LinkItem::class;
+
+  /**
+   * Constructs a new LinkItemNormalizer object.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   Entity type manager service.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager) {
+    $this->entityTypeManager = $entity_type_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function normalize($object, $format = NULL, array $context = array()) {
+    $link = parent::normalize($object, $format, $context);
+    $url = parse_url($link['uri']);
+    if (!isset($url['scheme']) || $url['scheme'] !== 'entity') {
+      return $link;
+    }
+    $path = isset($url['path']) ? $url['path'] : '';
+    list($target_entity_type_id, $target_entity_id) = explode('/', $path);
+    if ($this->entityTypeManager->hasDefinition($target_entity_type_id)) {
+      if ($target_entity = $this->entityTypeManager->getStorage($target_entity_type_id)->load($target_entity_id)) {
+        $link['target_uuid'] = $target_entity->uuid();
+      }
+    }
+    return $link;
+  }
+
+}
diff --git a/core/modules/menu_link_content/src/MenuLinkContentServiceProvider.php b/core/modules/menu_link_content/src/MenuLinkContentServiceProvider.php
new file mode 100644
index 0000000..66aabfd
--- /dev/null
+++ b/core/modules/menu_link_content/src/MenuLinkContentServiceProvider.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Drupal\menu_link_content;
+
+use Drupal\Core\DependencyInjection\ServiceModifierInterface;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\menu_link_content\Normalizer\HalMenuLinkContentNormalizer;
+use Drupal\menu_link_content\Normalizer\MenuLinkContentNormalizer;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Creates a service modifier to add a menu link content normalizer.
+ */
+class MenuLinkContentServiceProvider implements ServiceModifierInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function alter(ContainerBuilder $container) {
+    $modules = $container->getParameter('container.modules');
+    if (isset($modules['hal'])) {
+      // Add a hal normalizer service for menu-link-content entities.
+      $service_definition = new Definition(HalMenuLinkContentNormalizer::class, [
+        new Reference('rest.link_manager'),
+        new Reference('entity.manager'),
+        new Reference('module_handler'),
+      ]);
+      // The priority must be higher than that of
+      // serializer.normalizer.entity.hal in hal.services.yml.
+      $service_definition->addTag('normalizer', array('priority' => 40));
+      $container->setDefinition('menu_link_content.normalizer.menu_link_content.hal', $service_definition);
+    }
+    if (isset($modules['serialization'])) {
+      // Add a generic normalizer service for menu-link-content entities.
+      $service_definition = new Definition(MenuLinkContentNormalizer::class, [
+        new Reference('entity.manager'),
+      ]);
+      // The priority must be higher than that of
+      // serializer.normalizer.content_entity in serialization.services.yml.
+      $service_definition->addTag('normalizer', array('priority' => 10));
+      $container->setDefinition('menu_link_content.normalizer.menu_link_content', $service_definition);
+    }
+  }
+
+}
diff --git a/core/modules/menu_link_content/src/Normalizer/HalMenuLinkContentNormalizer.php b/core/modules/menu_link_content/src/Normalizer/HalMenuLinkContentNormalizer.php
new file mode 100644
index 0000000..ed66761
--- /dev/null
+++ b/core/modules/menu_link_content/src/Normalizer/HalMenuLinkContentNormalizer.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace Drupal\menu_link_content\Normalizer;
+
+use Drupal\Component\Plugin\PluginBase;
+use Drupal\Component\Utility\NestedArray;
+use Drupal\hal\Normalizer\ContentEntityNormalizer;
+use Drupal\hal\Normalizer\EntityReferenceEmbeddingTrait;
+use Drupal\menu_link_content\MenuLinkContentInterface;
+
+/**
+ * A normalizer to handle menu-link content links to entities.
+ */
+class HalMenuLinkContentNormalizer extends ContentEntityNormalizer {
+
+  use EntityReferenceEmbeddingTrait;
+
+  /**
+   * Psuedo field name for embedding parent target entity.
+   *
+   * @var string
+   */
+  const PSUEDO_PARENT_FIELD_NAME = 'menu_link_content_parent_entity';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $supportedInterfaceOrClass = MenuLinkContentInterface::class;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function normalize($entity, $format = NULL, array $context = array()) {
+    $normalized = parent::normalize($entity, $format, $context);
+    if (isset($normalized['parent']) && is_array($normalized['parent'])) {
+      foreach ($normalized['parent'] as $parent) {
+        // Menu link parent could be any plugin ID. We are only concerned with
+        // those that are also menu_link_content entities.
+        if (strpos($parent['value'], PluginBase::DERIVATIVE_SEPARATOR) !== FALSE) {
+          list($plugin_id, $parent_uuid) = explode(PluginBase::DERIVATIVE_SEPARATOR, $parent['value']);
+          if ($plugin_id === 'menu_link_content' && $parent_entity = $this->entityManager->loadEntityByUuid('menu_link_content', $parent_uuid)) {
+            // This entity has a parent menu link entity, we embed it.
+            $normalized = NestedArray::mergeDeep($normalized, $this->embedTargetEntity($entity, $parent_entity, self::PSUEDO_PARENT_FIELD_NAME, $this->serializer, $this->linkManager, $format, $context));
+          }
+        }
+      }
+    }
+    return $normalized;
+  }
+
+}
diff --git a/core/modules/menu_link_content/src/Normalizer/MenuLinkContentNormalizer.php b/core/modules/menu_link_content/src/Normalizer/MenuLinkContentNormalizer.php
new file mode 100644
index 0000000..362d1ac
--- /dev/null
+++ b/core/modules/menu_link_content/src/Normalizer/MenuLinkContentNormalizer.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Drupal\menu_link_content\Normalizer;
+
+use Drupal\menu_link_content\MenuLinkContentInterface;
+use Drupal\serialization\Normalizer\ContentEntityNormalizer;
+
+/**
+ * Defines a generic normalizer for menu link content entities.
+ */
+class MenuLinkContentNormalizer extends ContentEntityNormalizer {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $supportedInterfaceOrClass = MenuLinkContentInterface::class;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function denormalize($data, $class, $format = NULL, array $context = array()) {
+    if (isset($data['link']) && is_array($data['link'])) {
+      foreach ($data['link'] as $key => $link) {
+        $url = parse_url($link['uri']);
+        if (!isset($url['scheme']) || $url['scheme'] !== 'entity') {
+          continue;
+        }
+        $path = isset($url['path']) ? $url['path'] : '';
+        list($target_entity_type_id, $target_entity_id) = explode('/', $path);
+        if (isset($link['target_uuid'])) {
+          if ($entity = $this->entityManager->loadEntityByUuid($target_entity_type_id, $link['target_uuid'])) {
+            $data['link'][$key]['uri'] = 'entity:' . $target_entity_type_id . '/' . $entity->id();
+          }
+        }
+      }
+    }
+    $entity = parent::denormalize($data, $class, $format, $context);
+    return $entity;
+  }
+
+}
diff --git a/core/modules/menu_link_content/tests/src/Kernel/Normalizer/HalMenuLinkContentNormalizerTest.php b/core/modules/menu_link_content/tests/src/Kernel/Normalizer/HalMenuLinkContentNormalizerTest.php
new file mode 100644
index 0000000..af00035
--- /dev/null
+++ b/core/modules/menu_link_content/tests/src/Kernel/Normalizer/HalMenuLinkContentNormalizerTest.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace Drupal\Tests\menu_link_content\Kernel\Normalizer;
+
+use Drupal\menu_link_content\Normalizer\HalMenuLinkContentNormalizer;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\menu_link_content\Entity\MenuLinkContent;
+use Drupal\node\Entity\Node;
+use Drupal\node\Entity\NodeType;
+
+/**
+ * Tests menu link content normalizer.
+ *
+ * @group menu_link_content
+ */
+class HalMenuLinkContentNormalizerTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'menu_link_content',
+    'serialization',
+    'rest',
+    'hal',
+    'node',
+    'user',
+    'text',
+    'system',
+    'link',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->installEntitySchema('node');
+    $this->installEntitySchema('user');
+    $this->installEntitySchema('menu_link_content');
+    $this->installSchema('system', 'router');
+    $this->container->get('router.builder')->rebuild();
+  }
+
+  /**
+   * Tests normalizing menu_link_content entities.
+   */
+  public function testMenuLinkNormalizer() {
+    $node_type = NodeType::create([
+      'type' => 'article',
+      'name' => 'Article',
+    ]);
+    $node_type->save();
+    $node = Node::create([
+      'type' => 'article',
+      'title' => 'Some node',
+      'status' => 1,
+      'uid' => 1,
+    ]);
+    $node->save();
+    $parent = MenuLinkContent::create([
+      'title' => 'A front page menu link',
+      'link' => [['uri' => 'internal:/']],
+      'menu_name' => 'tools',
+    ]);
+    $parent->save();
+    $link = MenuLinkContent::create([
+      'title' => 'A menu link to a node',
+      'link' => [['uri' => 'entity:node/' . $node->id()]],
+      'menu_name' => 'tools',
+      'parent' => 'menu_link_content:' . $parent->uuid(),
+    ]);
+    $link->save();
+    $serializer = $this->container->get('serializer');
+    $link_manager = $this->container->get('rest.link_manager');
+    $mock_field_uri = $link_manager->getRelationUri('menu_link_content', 'menu_link_content', 'link_target', []);
+    $parent_field_uri = $link_manager->getRelationUri('menu_link_content', 'menu_link_content', HalMenuLinkContentNormalizer::PSUEDO_PARENT_FIELD_NAME, []);
+    $node_url = $node->toUrl('canonical', ['absolute' => TRUE])->setRouteParameter('_format', 'hal_json')->toString();
+    $parent_url = $parent->toUrl('canonical', ['absolute' => TRUE])->setRouteParameter('_format', 'hal_json')->toString();
+    $context['included_fields'] = ['uuid'];
+    $embedded = $serializer->normalize($node, 'hal_json', $context);
+    $embedded_parent = $serializer->normalize($parent, 'hal_json', $context);
+    $normalized = $serializer->normalize($link, 'hal_json');
+    $this->assertEquals($node->uuid(), $normalized['link'][0]['target_uuid']);
+    $this->assertEquals([$embedded], $normalized['_embedded'][$mock_field_uri]);
+    $this->assertEquals([['href' => $node_url]], $normalized['_links'][$mock_field_uri]);
+    $this->assertEquals([$embedded_parent], $normalized['_embedded'][$parent_field_uri]);
+    $this->assertEquals([['href' => $parent_url]], $normalized['_links'][$parent_field_uri]);
+    $this->assertEquals('menu_link_content:' . $parent->uuid(), $normalized['parent'][0]['value']);
+
+    // Now we switch the URI to something else but it should still go back to
+    // the same node.
+    $normalized['link'][0]['uri'] = 'entity:node/' . ($node->id() + 1);
+    /** @var \Drupal\menu_link_content\MenuLinkContentInterface $denormalized */
+    $denormalized = $serializer->denormalize($normalized, MenuLinkContent::class, 'hal_json');
+    $this->assertEquals($link->getUrlObject(), $denormalized->getUrlObject());
+    $this->assertEquals($link->getParentId(), $denormalized->getParentId());
+  }
+
+}
diff --git a/core/modules/menu_link_content/tests/src/Kernel/Normalizer/MenuLinkContentNormalizerTest.php b/core/modules/menu_link_content/tests/src/Kernel/Normalizer/MenuLinkContentNormalizerTest.php
new file mode 100644
index 0000000..34781b9
--- /dev/null
+++ b/core/modules/menu_link_content/tests/src/Kernel/Normalizer/MenuLinkContentNormalizerTest.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace Drupal\Tests\menu_link_content\Kernel\Normalizer;
+
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\menu_link_content\Entity\MenuLinkContent;
+use Drupal\node\Entity\Node;
+use Drupal\node\Entity\NodeType;
+
+/**
+ * Tests generic menu link content normalizer.
+ *
+ * @group menu_link_content
+ */
+class MenuLinkContentNormalizerTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'menu_link_content',
+    'serialization',
+    'rest',
+    'node',
+    'user',
+    'text',
+    'system',
+    'link',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->installEntitySchema('node');
+    $this->installEntitySchema('user');
+    $this->installEntitySchema('menu_link_content');
+    $this->installSchema('system', 'router');
+    $this->container->get('router.builder')->rebuild();
+  }
+
+  /**
+   * Tests normalizing menu_link_content entities.
+   */
+  public function testMenuLinkNormalizer() {
+    $node_type = NodeType::create([
+      'type' => 'article',
+      'name' => 'Article',
+    ]);
+    $node_type->save();
+    $node = Node::create([
+      'type' => 'article',
+      'title' => 'Some node',
+      'status' => 1,
+      'uid' => 1,
+    ]);
+    $node->save();
+    $parent = MenuLinkContent::create([
+      'title' => 'A front page menu link',
+      'link' => [['uri' => 'internal:/']],
+      'menu_name' => 'tools',
+    ]);
+    $parent->save();
+    $link = MenuLinkContent::create([
+      'title' => 'A menu link to a node',
+      'link' => [['uri' => 'entity:node/' . $node->id()]],
+      'menu_name' => 'tools',
+      'parent' => 'menu_link_content:' . $parent->uuid(),
+    ]);
+    $link->save();
+    $serializer = $this->container->get('serializer');
+    $normalized = $serializer->normalize($link, 'xml');
+    $this->assertEquals($node->uuid(), $normalized['link'][0]['target_uuid']);
+
+    // Now we switch the URI to something else but it should still go back to
+    // the same node.
+    $normalized['link'][0]['link'] = 'entity:node/' . ($node->id() + 1);
+    /** @var \Drupal\menu_link_content\MenuLinkContentInterface $denormalized */
+    $denormalized = $serializer->denormalize($normalized, MenuLinkContent::class, 'json');
+    $this->assertEquals($link->getUrlObject(), $denormalized->getUrlObject());
+    $this->assertEquals($link->getParentId(), $denormalized->getParentId());
+  }
+
+}
