Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Serializer] Extra attributes name converter for XML when using ObjectNormalizer #34579

Closed
@monteiro

Description

@monteiro

Related: #32114

Description

Currently, using JSON is the facto standard and well supported for all normalizers.
What I found is that the XmlEncoder decodes/encodes the XML to array, but converts extra attributes with a @ prefix and uses # to identity the node value.

This conversion makes it extremely difficult to use the ObjectNormalizer and fill out DTOs with specific XML data instead of consuming just the array returned by the XmlEncoder.

Example:

The array:
['foo' => ['@bar' => 'value', '#' => 'baz']];
generates the following XML output:

<?xml version="1.0"?>
 <response>
     <foo bar="value">
        baz
     </foo>
</response>

What if I want to use my own DTOs?

Possible solution

Create a custom name converter that handles the extra attributes and XML node values. That way you can out of the box, use the ObjectNormalizer to set specific attributes in a specific object.

Example

We can use custom prefixes for attributes and node values, so we match them with our DTOs:

final class SpecialAttributesConverter implements NameConverterInterface
{
    const ATTRIBUTE_PREFIX          = 'attr';
    const NODE_VALUE_ATTRIBUTE_NAME = 'value';

    /**
     * {@inheritdoc}
     */
    public function normalize($propertyName): string
    {
        if (self::NODE_VALUE_ATTRIBUTE_NAME === substr($propertyName, 0, strlen(self::NODE_VALUE_ATTRIBUTE_NAME))) {
            return '#';
        }

        if (self::ATTRIBUTE_PREFIX === substr($propertyName, 0, strlen(self::ATTRIBUTE_PREFIX))) {
            return '@' . substr($propertyName, strlen(self::ATTRIBUTE_PREFIX));
        }

        return $propertyName;
    }

    /**
     * {@inheritdoc}
     */
    public function denormalize($propertyName): string
    {
        if (0 === strpos($propertyName, '#')) {
            return self::NODE_VALUE_ATTRIBUTE_NAME;
        }

        if (0 === strpos($propertyName, '@')) {
            return self::ATTRIBUTE_PREFIX . substr($propertyName, 1);
        }

        return $propertyName;
    }
}

By using this name converter, we can configure the ObjectNormalizer:

$objectNormalizer  = new ObjectNormalizer(null, new SpecialAttributesConverter(), null, $phpDocExtractor);

and pass it to the serializer.

If we had this name converter already in the serializer package, we could create documentation that supported the integration between the XmlEncoder and the ObjectNormalizer like JSON is already supported.

Would be great to get your feedback and if needed, I could create all the documentation and PR needed and improve current implementation (like passing the attribute/node value naming to the name converter constructor for example).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions