eca_render: Not all format encoders/decoders picked up by serialize/unserialize action.

Created on 7 April 2025, 10 days ago

Problem/Motivation

Some format encoders/decoders from Core are not detected by the Serialize and Unserialize actions (from the ECA Render module) when populating the options in the action configuration form.
This makes it difficult to select certain formats e.g. XML.

This is caused by the code in \Drupal\eca_render\Plugin\Action\Serialize::buildConfigurationForm, where the available format encoders are collected by checking for a FORMAT class constant, which may not exist in some encoders. See code snippet.

    $format_options = [];
    // Need to fetch available formats from the protected properties of
    // the serialization service. This is an ugly approach, but the only one
    // that is available. It is a valid approach since it's declared as
    // a "public" API.
    // @see \Drupal\serialization\RegisterSerializationClassesCompilerPass::process()
    $encoders = (function () {
      return (function () {
        // @phpstan-ignore-next-line
        return $this->encoders;
        // @phpstan-ignore-next-line
      })(...)->call($this->encoder);
    })(...)->call($this->serializer);
    foreach ($encoders as $encoder) {
      $constant_name = get_class($encoder) . '::FORMAT';
      if (defined($constant_name)) {
        $format_options[constant($constant_name)] = constant($constant_name);
      }
    }

For example, the Drupal Core Serialization XML encoder \Drupal\serialization\Encoder\XmlEncoder
is not a subclass of the Symfony XmlEncoder; it is implemented as a wrapper class instead. Hence, it doesn't follow the Symfony convention of having a FORMAT constant. See code snippet.

/**
 * Adds XML support for serializer.
 *
 * This acts as a wrapper class for Symfony's XmlEncoder so that it is not
 * implementing NormalizationAwareInterface, and can be normalized externally.
 *
 * @internal
 *   This encoder should not be used directly. Rather, use the `serializer`
 *   service.
 */
class XmlEncoder implements SerializerAwareInterface, EncoderInterface, DecoderInterface {

Steps to reproduce

  1. On a fresh Drupal 10.x OR 11.x site, enable the modules REST, Serialization, ECA, ECA Render
  2. Create a new ECA flow
  3. Add "ECA Render: Unserialize" action to the ECA flow
  4. In the action configuration form, under the "format" option, note that only "json" or "defined by token" is selectable.
  5. Create a view, add a REST export display to the view.
  6. Note that Under the "Serializer" settings in the REST export view display, the formats "json" AND "xml" can be selected.

Proposed resolution

Use the same method that the core REST export views uses to detect serializer formats.
Specifically, in \Drupal\rest\Plugin\views\display\RestExport::create(), it appears that the list of available formats can be retrieved through $container->getParameter('serializer.format_providers').
However, to be absolutely certain that a format actually supports a decode/encode operation, you will still need to call $serializer->supportsDecoding() and $serializer->supportsEncoding() respectively.

Remaining tasks

User interface changes

None expected.

API changes

None expected.

Data model changes

None expected.

🐛 Bug report
Status

Active

Version

3.0

Component

Code

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @starlight-sparkle
  • 🇩🇪Germany jurgenhaas Gottmadingen

    This is not a bug. The action plugin has been designed to work with Symfony serializers. If there is another serializer from a different source, that's fine, but out of scope for this action plugin. We can handle this as a feature request to add a new action plugin for that special one.

  • We can handle this as a feature request to add a new action plugin for that special one.

    Understood. But it seems like this would result in a significant amount of feature overlap between the new action plugin and the existing one, because if a new action plugin was made that utilised Drupal Core's Serialization module to collect serializers, both Symfony and non-Symfony serializers would be dynamically collected as Drupal Core exposes both kinds in the same API.

  • 🇩🇪Germany jurgenhaas Gottmadingen

    Well, Drupal serialization is a module of its own, but this plugin builds upon the Symfony serializer which is always present. I don't know why Drupal core decided to go that route, but we were looking for something that always works and doesn't impose new dependencies. If we wanted to build something that requires the serialization module, then that would have to go into a new submodule which requires that to be enabled as well.

Production build 0.71.5 2024