Как правильно провести десериализацию xml файла с вложенными объектами при помощи Symfony Serializer?

Есть CommerceML файл, который приходит от 1С, в нем указываются товарные группы в виде:
Товарные группы из import.xml

<Группы>
			<Группа>
				<Ид>58a742d8-dcc4-11ec-9656-fa163ec2d0ed</Ид>
				<Наименование>Ковры</Наименование>
				<Группы>
					<Группа>
						<Ид>5df7cfa0-dcc4-11ec-9656-fa163ec2d0ed</Ид>
						<Наименование>Merinos</Наименование>
						<Группы>
							<Группа>
								<Ид>6a0d73a8-dcc4-11ec-9656-fa163ec2d0ed</Ид>
								<Наименование>Buhara</Наименование>
								<Группы>
									<Группа>
										<Ид>6a0d73a8-dcc4-11ec-9656-fa163ec2d0ez</Ид>
										<Наименование>Fourth Level Children</Наименование>
									</Группа>
									<Группа>
										<Ид>648a64cc-dcc4-11ec-9656-fa163ec2d0ex</Ид>
										<Наименование>Fourth Level Children</Наименование>
									</Группа>
								</Группы>
							</Группа>
							<Группа>
								<Ид>648a64cc-dcc4-11ec-9656-fa163ec2d0ed</Ид>
								<Наименование>Crystal</Наименование>
							</Группа>
						</Группы>
					</Группа>
					<Группа>
						<Ид>5df7cfa0-dcc4-11ec-9656-fa163ec2d0zd</Ид>
						<Наименование>Другое</Наименование>
					</Группа>
				</Группы>
			</Группа>
		</Группы>



Данная структура представляет из себя иерархию, и я хочу получить подобную структуру в виде DTO.

Для этого я создал сам GroupDto
GroupDto.php

<?php
namespace App\Services\CommerceML\Dto;

use Spatie\DataTransferObject\DataTransferObject;
use Symfony\Component\Serializer\Annotation\SerializedName;

class ≠ extends DataTransferObject
{
    /**
     * @SerializedName("Ид")
     */
    public string $id = '';

    /**
     * @SerializedName("Наименование")
     */
    public string $name = '';

    /**
     * @SerializedName("Группы")
     */   
    public array $childrens = [];
}



Важный момент public array $childrens = []; в DTO должен представлять из себя массив таких же DTO как и родитель, то есть класс GroupDto.

Собственно, сам сериализатор настроен так:

$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$extractor = new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]);
$metadataAwareNameConverter = new MetadataAwareNameConverter($classMetadataFactory);
$encoders = [new XmlEncoder()];
 $normalizers = [new ArrayDenormalizer(), new ObjectNormalizer($classMetadataFactory, $metadataAwareNameConverter, null ,$extractor)];
        
$serializer = new Serializer($normalizers, $encoders);


И вызов сериализатора:
$xml = simplexml_load_file(storage_path('1cExchange/1/import.xml'));
foreach ($xml->Классификатор->Группы as $group) {
      dd($serializer->deserialize($group->Группа->asXML(), GroupDto::class, 'xml', ['flatten_nested_attributes' => true]));
}


Но в таком случае получаю такой вывод:
Вывод
^ App\Services\CommerceML\Dto\GroupDto {#628 ▼
  #exceptKeys: []
  #onlyKeys: []
  +id: "58a742d8-dcc4-11ec-9656-fa163ec2d0ed"
  +name: "Ковры"
  +childrens: array:1 [▼
    "Группа" => array:2 [▼
      0 => array:3 [▼
        "Ид" => "5df7cfa0-dcc4-11ec-9656-fa163ec2d0ed"
        "Наименование" => "Merinos"
        "Группы" => array:1 [▶]
      ]
      1 => array:2 [▼
        "Ид" => "5df7cfa0-dcc4-11ec-9656-fa163ec2d0zd"
        "Наименование" => "Pepsi"
      ]
    ]
  ]
}


Хотелось бы, чтобы массив children был содержал в себе таки же GroupDto сформированные, а не чистые массивы, можно ли это как-то сделать вообще?
  • Вопрос задан
  • 198 просмотров
Решения вопроса 1
BoShurik
@BoShurik Куратор тега Symfony
Symfony developer
В общем случае надо добавить аннотацию @var GroupDto[] чтоб сериалайзер понимал, что там типизированный массив.
Плюс придется написать свой Normalizer, т.к. при нормализации родительской группы вы ручками "игнорируете" ноду "Группа"
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы