You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Symfony Serializer为类对象添加/更新XML属性并设置primary标识

Solution: Handle XML Attributes with Object-Oriented Classes & Setters

Great question! You don’t have to switch to arrays to manage XML attributes like primary with Symfony Serializer. You can keep your Number and Contact classes as proper objects, use setter methods to control the primary attribute, and configure the serializer to map class properties to XML attributes. Here’s how to do it step by step:

1. Update the Number Class to Support the primary Attribute

First, add a $primary property to your Number class, along with corresponding getter/setter methods. We’ll also use Symfony Serializer annotations to tell the encoder that this property should be rendered as an XML tag attribute:

use Symfony\Component\Serializer\Annotation\XmlAttribute;

class Number {
    private $Number;
    private $Type;
    private $primary; // Add this property to store the primary attribute value

    // Getters
    public function getNumber() {
        return $this->Number;
    }

    public function getType() {
        return $this->Type;
    }

    /**
     * @XmlAttribute // This annotation tells the serializer to treat this as an XML tag attribute
     */
    public function getPrimary() {
        return $this->primary ?? '0'; // Default to '0' if the value isn't set
    }

    // Setters (supports method chaining)
    public function setNumber($number) {
        $this->Number = $number;
        return $this;
    }

    public function setType($type) {
        $this->Type = $type;
        return $this;
    }

    public function setPrimary($primary) {
        $this->primary = $primary;
        return $this; // Enable chained calls like setNumber()->setPrimary()
    }
}

2. Fix the Contact Class Collection Handling

Your original addNumber method wrapped numbers in an extra ['Number' => [...]] array, which caused inconsistent structure. Update the Contact class to directly store an array of Number objects, and use annotations to define how the collection should serialize to XML:

use Symfony\Component\Serializer\Annotation\XmlList;

class Contact {
    private $Name;
    /** @var Number[] */
    private $Numbers = [];

    // Getters
    public function getName() {
        return $this->Name;
    }

    /**
     * @XmlList(inline = false, entry = "Number")
     * This tells the serializer to wrap numbers in a <Numbers> parent tag,
     * with each entry rendered as a <Number> tag
     */
    public function getNumbers() {
        return $this->Numbers;
    }

    // Setters
    public function setName($name) {
        $this->Name = $name;
        return $this;
    }

    public function setNumbers($numbers) {
        $this->Numbers = $numbers;
        return $this;
    }

    public function addNumber(Number $number) {
        $this->Numbers[] = $number; // Directly add the Number object to the collection
    }
}

3. Update Serializer Setup to Enable Annotations

To make the serializer recognize the annotations, you need to add an annotation loader to the ObjectNormalizer:

// Symfony Serializer experiment
require $_SERVER['DOCUMENT_ROOT'].'/libraries/symfony/vendor/autoload.php';
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
use Doctrine\Common\Annotations\AnnotationReader;

// Setup annotation support for serializer metadata
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$objectNormalizer = new ObjectNormalizer($classMetadataFactory);

// Serializer setup
$xmlEncoder = new XmlEncoder();
$xmlEncoder->setRootNodeName('Contact');
$encoders = [$xmlEncoder, new JsonEncoder()];
$normalizers = [$objectNormalizer];
$serializer = new Serializer($normalizers, $encoders);

4. Test the Updated Code

Now you can use setter methods to set the primary attribute with method chaining, and the serializer will consistently handle both serialization and deserialization with objects (no more mixed arrays/objects):

// Create a contact with a primary number
$contact = new Contact();
$contact->setName('baa');

$primaryNumber = new Number();
$primaryNumber->setNumber('07878386123')
              ->setType('Mobile')
              ->setPrimary('1'); // Use the setter to define the primary attribute
$contact->addNumber($primaryNumber);

// Deserialize your sample XML into the Contact object (now creates Number objects, not arrays)
$data = <<<EOF
<Contact>
    <Name>foo</Name>
    <Numbers>
        <Number primary='1'>
            <Number>07878386459</Number>
            <Type>Mobile</Type>
        </Number>
        <Number primary='0'>
            <Number>02380860835</Number>
            <Type>Landline</Type>
        </Number>
    </Numbers>
</Contact>
EOF;
$contact = $serializer->deserialize($data, Contact::class, 'xml', ['object_to_populate' => $contact]);

// Add another number with primary=0
$newNumber = new Number();
$newNumber->setNumber('02387345123')
          ->setType('Landline')
          ->setPrimary('0');
$contact->addNumber($newNumber);

// Serialize to XML - all <Number> tags will have the primary attribute
$xmlContent = $serializer->serialize($contact, 'xml');
$dom = new domDocument( '1.0', 'utf-8' );
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$result = $dom->loadXML($xmlContent);
$xmlContentFormatted=htmlentities($dom->saveXML());
?>
<pre lang=xml><?= $xmlContentFormatted ?></pre>

Key Explanations

  • Why you saw mixed arrays/objects before: When you deserialized the XML with primary attributes, the original Number class had no corresponding property for primary, so the serializer couldn’t map it to an object and fell back to an array. Now with the $primary property and @XmlAttribute annotation, it correctly maps to Number objects.
  • Setter method support: The setPrimary method lets you control the attribute value directly via object methods, including the chained calls you wanted.
  • No need for arrays: You can keep your object-oriented structure intact — arrays aren’t required to handle XML attributes with Symfony Serializer.

内容的提问来源于stack exchange,提问作者Nick Weavers

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:40:58