diff --git a/metatag.services.yml b/metatag.services.yml
index ec8a204..6335ca2 100644
--- a/metatag.services.yml
+++ b/metatag.services.yml
@@ -38,3 +38,9 @@ services:
     arguments: ['@?console.extension_manager', '@?console.renderer']
     tags:
       - { name: drupal.generator }
+
+  # Priority of the metatag normalizer must be higher than other general-purpose typed data and field item normalizers.
+  metatag.normalizer.metatag_field_item:
+    class: Drupal\metatag\Normalizer\FieldItemNormalizer
+    tags:
+      - { name: normalizer, priority: 30 }
diff --git a/src/Normalizer/FieldItemNormalizer.php b/src/Normalizer/FieldItemNormalizer.php
new file mode 100644
index 0000000..423d5a1
--- /dev/null
+++ b/src/Normalizer/FieldItemNormalizer.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace Drupal\metatag\Normalizer;
+
+use Drupal\Core\Field\FieldItemInterface;
+use Symfony\Component\Serializer\Exception\InvalidArgumentException;
+use Drupal\serialization\Normalizer\NormalizerBase;
+
+/**
+ * Converts the Metatag field item object structure to METATAG array structure.
+ */
+class FieldItemNormalizer extends NormalizerBase {
+
+  /**
+   * The interface or class that this Normalizer supports.
+   *
+   * @var string
+   */
+  protected $supportedInterfaceOrClass = 'Drupal\metatag\Plugin\Field\FieldType\MetatagFieldItem';
+
+  /**
+   * Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize()
+   */
+  public function normalize($field_item, $format = NULL, array $context = []) {
+    $values = $field_item->toArray();
+
+    // Convert tokens if there are any.
+    $token = \Drupal::token();
+    $values['value'] = $token->replace($values['value']);
+
+    if (isset($context['langcode'])) {
+      $values['lang'] = $context['langcode'];
+    }
+
+    // Detect any data format that could be JSON (since we can define our own).
+    if (strpos($format, 'json') !== false) {
+      $tags = [];
+      // Get serialized value and break it into an array of tags with values.
+      $serialized_value = $field_item->get('value')->getValue();
+      $tags = unserialize($serialized_value);
+    }
+
+    // Convert tokens if there are any.
+    $tags = array_map(function ($s) {
+      $token = \Drupal::token();
+      return $token->replace($s);
+    }, $tags);
+
+    // Mock the field array similar to the other fields.
+    return array(
+      $field_item->getfieldDefinition()->getName() => [
+        [
+          'value' => $tags,
+          'lang' => $values['lang']
+        ]
+      ]
+    );
+  }
+}
