Как десериализовать XML в объект, который содержит коллекцию массивов в php Symfony - PullRequest
0 голосов
/ 19 апреля 2019

У меня есть XML в формате

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

`

У меня есть объект php, в который я хочу десериализоваться.

  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;
}

Когда я делаю

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

Он дает мне POS-объект, но вместо набора объектов POS_Source он дает массив ниже.

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

Как я могу заставить эту работу заполнять дерево объектов до самого дна. Когда я сериализую из структуры объекта в XML, он прекрасно работает.

Ответы [ 3 ]

0 голосов
/ 22 апреля 2019

При десериализации объектов, содержащих другие объекты, вы должны предоставить ObjectNormalizer экстрактор типов, который определяет тип вложенных объектов.

use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
// ...
$normalizers = [
  new DateTimeNormalizer(),
  new ArrayDenormalizer(),
  new PropertyNormalizer(),
  new ObjectNormalizer($classMetadataFactory, $metadataAwareNameConverter, null, new ReflectionExtractor()), // added type extractor as fourth argument
];

См. Также официальную документацию по этой теме..

0 голосов
/ 24 апреля 2019

Вот минимальный рабочий пример десериализации XML для одного экземпляра POS с ArrayCollection из POS_Source экземпляров.Я выбросил все нормализаторы и т. Д., Которые не нужны для десериализации этого конкретного 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);

Отпечатки:

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"
          "#" => ""
        ]
      }
    ]
  }
}
0 голосов
/ 22 апреля 2019

Это частичный ответ, а не решение.

Таким образом, похоже, что десериализация не поддерживает встроенные объекты php и что вы создали собственный метод десериализации.

Я все еще работаю над решением, но короткий ответ - вам нужно перебрать нормализованный массив и затем попытаться сопоставить имена свойств. Я пытаюсь найти метод для запроса объекта только для тех свойств, которые включены в аннотацию блочного документа группы сериализации.

...