简体   繁体   中英

How do I add/update xml attributes with Symfony Serializer using my class/object?

I have the following code example which defines a Contact class that can hold a name and a list of phone numbers. What I want to be able to do is set an attribute of "primary" on all the numbers, where the value for it is '1' for one of them arbitrarily, and '0' for the rest.

I'd really like to do this using a class method but couldn't find a way to do this using a setter - is it possible somehow?

Here's my code. In it I:

  1. Create my objects and set their properies
  2. print_r the Contact object to see how it is stored
  3. Deserialize the xml that I'd like to be able to generate into the same Contact object
  4. print_r the Contact object to see how it is stored
  5. Add another number using setter
  6. print_r the Contact object to see how it is stored
  7. Serialize to xml and see how the mixture of objects and arrays are treated
  8. print the xml

<?php
// 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\Normalizer\ArrayDenormalizer;

Class Number { 
    private $Number;
    private $Type;
    // Getters    
    public function getNumber()
    {
        return $this->Number;
    }
    public function getType()
    {
        return $this->Type;
    }     
    // Setters    
    public function setNumber($number)
    {
        $this->Number = $number;

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

        return $this;
    }                 
} 

class Contact
{
    private $Name;
    private $Numbers = array();

    function __construct() {
        ${@Primary} = 0;    
    }

    // Getters
    public function getName()
    {
        return $this->Name;
    }
    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) {
        $this->Numbers['Number'][] = $number;
    }    
}

// Serializer setup

$xmlEncoder = new XmlEncoder();
$xmlEncoder->setRootNodeName('Contact');                
$encoders = array($xmlEncoder, new JsonEncoder());
$normalizers = array(new ObjectNormalizer());
$serializer = new Serializer($normalizers, $encoders);

// Create my object

$contact = new Contact();
$contact->setName('baa');
$number = new Number();
$number->setNumber('07878386123')->setType('Mobile');
$contact->addNumber($number);

// take a look at how it's stored

echo "<pre lang=xml>";
print_r($contact);
echo "</pre><br><br>";

// deserialize my desired xml into the same object

$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', array('object_to_populate' => $contact));

// take a look at how it's stored

echo "<pre lang=xml>";
print_r($contact);
echo "</pre><br><br>";

// Add another number

$number = new Number();
$number->setNumber('02387345123')->setType('Landline');
$contact->addNumber($number);

// take a look at how it's stored

echo "<pre lang=xml>";
print_r($contact);
echo "</pre><br><br>";

// serialize  to xml and see how the mixture of objects and arrays are treated

$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>

What I see in the output is:

2)

Contact Object
(
    [Name:Contact:private] => baa
    [Numbers:Contact:private] => Array
        (
            [Number] => Array
                (
                    [0] => Number Object
                        (
                            [Number:Number:private] => 07878386123
                            [Type:Number:private] => Mobile
                        )   
                )   
        )   
)

4)

Contact Object
(
    [Name:Contact:private] => foo
    [Numbers:Contact:private] => Array
        (
            [Number] => Array
                (
                    [0] => Array
                        (
                            [@primary] => 1
                            [Number] => 07878386459
                            [Type] => Mobile
                        )    
                    [1] => Array
                        (
                            [@primary] => 0
                            [Number] => 02380860835
                            [Type] => Landline
                        )   
                )   

)

6) Mixture of objects and arrays. Could I code my Number class so that it uses an array rather than properties and have another method for setting attribute? (eg

$number->setNumber('07878386123')->setType('Mobile')->addAttr(array('primary' => '1'));

Contact Object
(
    [Name:Contact:private] => foo
    [Numbers:Contact:private] => Array
        (
            [Number] => Array
                (
                    [0] => Array
                        (
                            [@primary] => 1
                            [Number] => 07878386459
                            [Type] => Mobile
                        )    
                    [1] => Array
                        (
                            [@primary] => 0
                            [Number] => 02380860835
                            [Type] => Landline
                        )

                    [2] => Number Object
                        (
                            [Number:Number:private] => 02387345123
                            [Type:Number:private] => Landline
                        )    
                )    
        )    
)    

8)

<?xml version="1.0"?>
<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>
    <Number>
      <Number>02387345123</Number>
      <Type>Landline</Type>
    </Number>
  </Numbers>
</Contact>

When deserializing from XML that has tags that use attributes, the php objects use arrays, so it would appear that if you want to start out with a php representation that can be given attributes you need to use arrays.

Quite an unsophisticated hack, but managed to find a way to make it work. The modified example now does what I need:

<?php
// 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\Normalizer\ArrayDenormalizer;

// build datastructure as array
Class Number { 
    private $data;

    public function __construct(array $attributes) {
        $data = ['Number'];
        foreach ($attributes as $name => $value) {
            $this->data['Number']['@'.$name] = $value;
        }
    }

    // Getters    
    public function getNumber()
    {
        return $this->data['Number']['Number'];
    }
    public function getType()
    {
        return $this->data['Number']['Type'];
    }

    public function get() 
    {
        return $this->data['Number'];    
    }         
    // Setters    
    public function setNumber($number)
    {
        $this->data['Number']['Number'] = $number;

        return $this;
    } 
    public function setType($type)
    {
        $this->data['Number']['Type'] = $type;

        return $this;
    }

}

class Contact
{
    private $data;

    // Getters
    public function getName()
    {
        return $this->data['Contact']['Name'];
    }
    public function getNumbers()
    {
        return $this->data['Contact']['Numbers'];
    }
    public function get() 
    {
        return $this->data['Contact']; // this is the top level so return the Contact element, not data.    
    }      
    // Setters
    public function setName($name)
    {
        $this->data['Contact']['Name'] = $name;        

        return $this;        
    }
    public function setNumbers($numbers)
    {
        $this->data['Contact']['Numbers'] = $numbers;        

        return $this;        
    }
    public function addNumber($number) {
        $this->data['Contact']['Numbers']['Number'][] = $number;
    }    
}

// Serializer setup

$xmlEncoder = new XmlEncoder();
$xmlEncoder->setRootNodeName('Contact');                
$encoders = array($xmlEncoder, new JsonEncoder());
$normalizers = array(new ObjectNormalizer());
$serializer = new Serializer($normalizers, $encoders);
$encoder = new \Symfony\Component\Serializer\Encoder\XmlEncoder();

// Create my object

$contact = (new Contact)->setName('Foo Bar');
$number = (new Number(array('primary'=>'1')))->setNumber('07878386123')->setType('Mobile');
$number_data = $number->get();
$contact->addNumber($number_data);
$number = (new Number(array('primary'=>'0')))->setNumber('02380876345')->setType('Landline');
$number_data = $number->get();
$contact->addNumber($number_data);

echo "<pre lang=xml>";
print_r($contact);
echo "</pre><br><br>";

$xmlContent = $serializer->serialize($contact->get(), '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> 

Output:

Contact Object
(
    [data:Contact:private] => Array
        (
            [Contact] => Array
                (
                    [Name] => Foo Bar
                    [Numbers] => Array
                        (
                            [Number] => Array
                                (
                                    [0] => Array
                                        (
                                            [@primary] => 1
                                            [Number] => 07878386123
                                            [Type] => Mobile
                                        )

                                    [1] => Array
                                        (
                                            [@primary] => 0
                                            [Number] => 02380876345
                                            [Type] => Landline
                                        )
                                )
                        )
                )
        )
)


<?xml version="1.0"?>
<Contact>
  <Name>Foo Bar</Name>
  <Numbers>
    <Number primary="1">
      <Number>07878386123</Number>
      <Type>Mobile</Type>
    </Number>
    <Number primary="0">
      <Number>02380876345</Number>
      <Type>Landline</Type>
    </Number>
  </Numbers>
</Contact>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM