Score:2

Creating your own Node Entity Normalizer

br flag

I am trying to create my own Node Entity Normalizer in Drupal 10 to get rid of the unnecessary JSON structure that comes out of the box. For example, instead of my API's returning

{
  "nid": [
    {
      "value": 7
    }
  ]
}

I want to make it return

{
  "nid": 7
}

So I've tried the following options in my custom module:

  1. Typed Data Interface

example_normalizer.services.yml

services:
  example_normalizer.typed_data:
    class: Drupal\example_normalizer\Normalizer\CustomTypedDataNormalizer
    tags:
    - { name: normalizer, priority: 2 }

src/Normalizer/CustomTypedDataNormalizer.php

<?php
namespace Drupal\example_normalizer\Normalizer;
use Drupal\serialization\Normalizer\NormalizerBase;
/**
 * Converts typed data objects to arrays.
 */
class CustomTypedDataNormalizer extends NormalizerBase {
  /**
 * The interface or class that this Normalizer supports.
 *
 * @var string
 */
  protected $supportedInterfaceOrClass = 'Drupal\Core\TypedData\TypedDataInterface';
  /**
 * {@inheritdoc}
 */
  public function normalize($object, $format = NULL, array $context = array()) {
    $value = $object->getValue();
    if (isset($value[0]) && isset($value[0]['value'])) {
      $value = $value[0]['value'];
    }
    return $value;
  }
}

and

  1. Custom Node Entity Normalizer

example_normalizer.services.yml

services:
  example_normalizer.node_entity:
    class: Drupal\example_normalizer\Normalizer\NodeEntityNormalizer
    arguments: ['@entity.manager']
    tags:
    - { name: normalizer, priority: 8 }

src/Normalizer/NodeEntityNormalizer.php

<?php
namespace Drupal\example_normalizer\Normalizer;
use Drupal\serialization\Normalizer\ContentEntityNormalizer;
use Drupal\Core\Datetime\DrupalDateTime;
/**
 * Converts the Drupal entity object structures to a normalized array.
 */
class NodeEntityNormalizer extends ContentEntityNormalizer {
  /**
 * The interface or class that this Normalizer supports.
 *
 * @var string
 */
  protected $supportedInterfaceOrClass = 'Drupal\node\NodeInterface';
  /**
 * {@inheritdoc}
 */
  public function normalize($entity, $format = NULL, array $context = array()) {
    // Similar logic as above
  }
}

However, in both cases, when I clear the cache I am getting the error

PHP Fatal error:  Uncaught Error: Class "Drupal\example_normalizer\Normalizer\NodeEntityNormalizer" not found in /var/www/html/web/core/lib/Drupal/Component/DependencyInjection/Container.php:262

What am I missing in my module that this is not getting picked up?

I'd like to be able to make a call like

$serializer = \Drupal::service('serializer')
$custom_json = $serializer->serialize($entity, 'json', ['plugin_id' => 'my_custom_normalizer']
Kevin avatar
in flag
You've cleared the cache? Are these pastes correct? The second example YAML does not validate.
apaderno avatar
us flag
The *example_normalizer.services.yml* file is not correctly formatted, in both the cases. The indentation must be two spaces, not a single space. Patrick Kenny's answer does not explain why you get that error, but it explains how to change the normalizer used by Drupal core.
BlondeSwan avatar
br flag
@Kevin No, I can't clear the cache, that is when I am getting the DependencyInjection error. And the pastes were not correct, I just updated them though.
BlondeSwan avatar
br flag
@apaderno I just updated them, they are correct in my actual module (I have an auto formatter). My ultimate goal is to be able to point to my own plugin_id when I call Drupal's serialize function.
Score:1
cn flag

JSON normalization is not declared in services.yml. Instead, you want to use MyModuleServiceProvider.php to override the core class.

This is an example from my site overriding FieldItemNormalizer:

<?php
    
namespace Drupal\my_module;

use Drupal\Core\DependencyInjection\ServiceProviderBase;
use Drupal\Core\DependencyInjection\ServiceProviderInterface;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

class MyModuleServiceProvider extends ServiceProviderBase implements ServiceProviderInterface {

  /**
   * {@inheritDoc}
   */
  public function alter(ContainerBuilder $container): void {
    $jsonapi_definition = $container->getDefinition('serializer.normalizer.field_item.jsonapi');
    $jsonapi_definition->setClass('Drupal\my_module\Normalizer\MyModuleFieldItemNormalizer');
}

Here's a general guide to overriding core services.

BlondeSwan avatar
br flag
I don't think that is what I want to actually do. I'm not looking to override the core class completely, I just want to create a new normalizer so that I can serialize with a call like: \Drupal::service('serializer')->serialize($entity, 'json', ['plugin_id' => 'my_custom_normalizer']
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.