Pete_Gore
Pete_Gore

Reputation: 634

Serialize XML document with attributes

I'm trying to serialize an XML document containing entities to insert into Doctrine MySQL database.

I got, for example, these two attributes in my entity :

The problem is that instead of something like this into my XML doc :

<company>
    <id>8888</id>
    <name>MyCompany</name>
</company>

I got something like this :

<company id="8888" name="MyCompany"/>

The XML is generated by an independant company I work with ; so I can't change it. So the Symfony2 serializer is creating an empty $company attribute :(

Is there a simple way to costumize the seralizing process like I want ? Or do I have to implement a complete independant method ?

Thanks a lot.

Upvotes: 3

Views: 6037

Answers (4)

Mirgen
Mirgen

Reputation: 170

Quite old, but I got to a much simpler solution using Serializator + xpath in SerializedName annotation, so this could be useful for someone.

Having for example this entry XML:

<root>
    <company id="123456"/>
</root>

Whe deserializing into an object you could use this annotation to populate the company id into "id" property:

/**
 * @Serializer\SerializedName(name="company/@id")
 */
public ?int $id = null;

PS: tested on Symfony 5.4

Upvotes: 0

Tac Tacelosky
Tac Tacelosky

Reputation: 3426

It's now much easier to serialize XML attributes by using the @SerializeName annotation with '@'.

In your Company entity, when defining $name, add

    /**
     * @ORM\Column(type="string", length=255)
     * @SerializedName('@name')
     */
    private $name;

Now when you serialize to XML, it will come out as a property, as expected.

I know the OP was actually asking about deserialization, but hope this helps someone who is searching.

Upvotes: 8

Pete_Gore
Pete_Gore

Reputation: 634

Okay so finally I tried to use the JMSSerializerBundle but my case is too complicated. I got many entities with several ManyToOne relations ; and I got bot standard and attributes values in my XML.

So I'll use your idea : create my complete whole Denormalizer. It will use the decoded XML and read it line by line, doing what it has to do (creating entities with Doctrine).

It's gonna be a huge process but the most simple one.

Thank you.

[EDIT] I finally found a pretty good solution. I registered the links between XML and my entity setters into a yaml table

company:
    @id: setCompanyId
    @name : setCompanyName
    address:
        @city: setAddressCity
        @street: setAddressStreet
...

Thanks to that, I can easily read my whole XML and, for each node/attribute value, find the setter name into the table and then do :

$company = new Company;
$company->setterNameFromTable($value);

Upvotes: 0

Touki
Touki

Reputation: 7525

I'd create a simple Denormalizer because attributes are already parsed by default XmlEncoder. It adds a special character @ in at the beggining of the key.

Without tweaking alot you could add a context parameter like use_attributes which your custom denormalizer can understand. Here's an example

use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;

class AttributesDenormalizer implements DenormalizerInterface
{
    public function __construct(DenormalizerInterface $delegate)
    {
        $this->delegate = $delegate;
    }

    public function denormalize($data, $class, $format = null, array $context = array())
    {
        if (!is_array($data) || !isset($context['use_attributes']) || true !== $context['use_attributes']) {
            return $this->delegate->denormalize($data, $class, $format, $context);
        }

        $attributes = array();

        foreach ($data as $key => $value) {
            if (0 === strpos($key, '@')) {
                $attributes[substr($key, 1)] = $value;
            }
        }

        if (empty($attributes)) {
            $attributes = $data;
        }

        return $this->delegate->denormalize($attributes, $class, $format, $context);
    }

    public function supportsDenormalization($data, $type, $format = null)
    {
        return $this->delegate->supportsDenormalization($data, $type, $format);
    }
}

And here is an example of usage

use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;

$xml = '<company id="8888" name="MyCompany"/>';

$encoders = array(new XmlEncoder());
$normalizers = array(new AttributesDenormalizer(new GetSetMethodNormalizer));

$serializer = new Serializer($normalizers, $encoders);
$serializer->deserialize($xml, 'Company', 'xml', array('use_attributes' => true));

Which results in

class Company#13 (2) {
  protected $id =>
  string(4) "8888"
  protected $name =>
  string(9) "MyCompany"
}

Upvotes: 5

Related Questions