phpsymfonydeserializationarraycollection

How to deserialize XML to an object that contains an array collection in php Symfony


I have XML in the format

<POS>
    <Source PseudoCCode="BOA" ISOCountry="US" AgentDutyCode="J114N">
        <RequestorID Type="11" ID="T921">
            <CompanyName Code="CP" CodeContext="123T"/>
        </RequestorID>
    </Source>
    <Source>
        <RequestorID Type="1" ID="34778"/>  
    </Source>
    <Source>
        <RequestorID Type="9" ID="ZF"/>
    </Source>
    <Source>
        <RequestorID Type="17" ID="mabaan"/>
    </Source>
</POS>

`

I have a php object that I want to deserialize into.

  class POS
  {
 /**
   * @ORM\OneToMany(targetEntity="POS_Source", mappedBy="POS", orphanRemoval=true)
 * @Groups("Include")
 */
private $Source;

public function __construct()
{
     $this->Source = new ArrayCollection();
}
/**
 * @return ArrayCollection|OTA_POS_Source[]
 */
public function getSource(): ArrayCollection
{
    return $this->Source;
}

public function addSource(POS_Source $source): self
{
    if (!$this->Source->contains($source)) {
        $this->Source[] = $source;
        $source->setPOS($this);
    }

    return $this;
}

public function removeSource(POS_Source $source): self
{
    if ($this->Source->contains($source)) {
        $this->Source->removeElement($source);
        // set the owning side to null (unless already changed)
        if ($source->getPOS() === $this) {
            $source->setPOS(null);
        }
    }

    return $this;
}

When I do

    $classMetadataFactory = new ClassMetadataFactory(
        new AnnotationLoader(new AnnotationReader())
    );

    $metadataAwareNameConverter = new MetadataAwareNameConverter($classMetadataFactory);

    $normalizers = [new DateTimeNormalizer(), new ArrayDenormalizer(),
        new PropertyNormalizer(), new ObjectNormalizer($classMetadataFactory, $metadataAwareNameConverter)];
    $encoders = [new XmlEncoder(), new JsonEncoder()];

    $serializer = new Serializer($normalizers, $encoders);

    $encoder = new XmlEncoder();

    $output[] = $encoder->decode($data,'xml');

    dump($output);


    /**
     * @var OTA_POS $pos
     */
    $pos = $serializer->deserialize($data,POS::class,'xml');

    $posSourceArray = $serializer->deserialize($pos->getSource(),'App\POS_Source[]','xml');

    dump($posSourceArray);

It gives me the POS object but rather than a collection of POS_Source objects it gives be an array below.

 POS {#839 ▼
   -id: null
   -Source: array:5 [▼
     0 => array:4 [▶]
     1 => array:1 [▶]
     2 => array:1 [▶]
     3 => array:1 [▶]
     4 => array:1 [▶]
   ]
 }

How can I make this work to populate the object tree all the way to the bottom. When I serialize from object structure to XML it works great.


Solution

  • Here is a minimal working example deserializing the XML to a single POS instance with an ArrayCollection of POS_Source instances. I threw away all normalizers etc. not essential for deserializing this particular XML.

    use Doctrine\Common\Collections\ArrayCollection;
    use Symfony\Component\Serializer\Encoder\XmlEncoder;
    use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
    use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
    use Symfony\Component\Serializer\Serializer;
    use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
    
    class POS
    {
        // ... just as in the question ...
    }
    
    /**
     * Minimal implementation of POS_Source for purposes of this deserialization example.
     */
    class POS_Source
    {
        private $RequestorID;
    
        public function setPOS(POS $POS)
        {
        }
    
        public function getRequestorID()
        {
            return $this->RequestorID;
        }
    
        public function setRequestorID($RequestorID)
        {
            $this->RequestorID = $RequestorID;
        }
    }
    
    $data = '<POS>
        <!-- ... the same XML as in the question ... -->
    </POS>';
    
    $normalizers = [
        new ArrayDenormalizer(),
        new ObjectNormalizer(null, null, null, new ReflectionExtractor())
    ];
    $encoders = [new XmlEncoder()];
    
    $serializer = new Serializer($normalizers, $encoders);
    
    $pos = $serializer->deserialize($data,POS::class,'xml');
    dump($pos);
    

    Prints:

    POS {#14
      -Source: Doctrine\Common\Collections\ArrayCollection {#11
        -elements: array:4 [
          0 => POS_Source {#17
            -RequestorID: array:3 [
              "@Type" => 11
              "@ID" => "T921"
              "CompanyName" => array:3 [
                "@Code" => "CP"
                "@CodeContext" => "123T"
                "#" => ""
              ]
            ]
          }
          1 => POS_Source {#27
            -RequestorID: array:3 [
              "@Type" => 1
              "@ID" => 34778
              "#" => ""
            ]
          }
          2 => POS_Source {#22
            -RequestorID: array:3 [
              "@Type" => 9
              "@ID" => "ZF"
              "#" => ""
            ]
          }
          3 => POS_Source {#25
            -RequestorID: array:3 [
              "@Type" => 17
              "@ID" => "mabaan"
              "#" => ""
            ]
          }
        ]
      }
    }