Symfony Serializer:反规范化(反序列化)笨拙的数组数据

时间:2017-12-28 02:26:56

标签: json symfony serialization symfony4

使用symfony/serializer版本4.我从一个看起来像这样的API获取JSON的数据

{
  "name": "Steves Book Shop",
  "book_1": "Lord of the Rings",
  "book_2": "The Hobbit",
  "book_[n]": "there can be any number of books"
}

我想反序列化为以下模型

class BookShop
{
    protected $name;

    /** @var  Book[] */
    protected $books;

    public function getBooks(): array
    {
        return $this->books;
    }

    public function setBooks(array $books)
    {
        $this->books = $books;
    }

    public function addBook(Book $book)
    {
        $this->books[] = $book;
    }
    // ... other code removed to save space   
}

class Book
{
    protected $title;
    // ... other code removed to save space 
}

在下面使用“清洁”JSON时,一切都按预期工作,我得到一个BookShop,并返回Book数组。

{
  "name": "Steves Book Shop",
  "books": [
      { "title": "Lord of the Rings" },
      { "title": "The Hobbit" }   
    ]
}

对于原始JSON进行非规范化的干净方法是什么,而不是令人烦恼的book_1book_2等。

我一直在尝试使用自定义的非规范化程序(DenormalizerInterface),我的解决方案看起来比您预期的要困难得多。

2 个答案:

答案 0 :(得分:0)

你应该对这些书使用ArrayCollection - 假设它们只是你应用程序中的另一个实体

答案 1 :(得分:0)

这是我最终得到的解决方案,我并不完全相信这是最好的方法,但它目前正在努力。任何反馈欢迎:

class StrangeDataDenormalizer extends Symfony\Component\Serializer\Normalizer\ObjectNormalizer
{
    protected function isStrangeDataInterface(string $type): bool
    {
        try {
            $reflection = new \ReflectionClass($type);

            return $reflection->implementsInterface(StrangeDataInterface::class);
        } catch (\ReflectionException $e) { // $type is not always a valid class name, might have extract junk like `[]`
            return false;
        }
    }

    public function denormalize($data, $class, $format = null, array $context = array())
    {
        $normalizedData = $this->prepareForDenormalization($data);

        $normalizedData = $class::prepareStrangeData($normalizedData);

        return parent::denormalize($normalizedData, $class, $format, $context);
    }

    public function supportsDenormalization($data, $type, $format = null)
    {
        return $this->isStrangeDataInterface($type);
    }
}

interface StrangeDataInterface
{
    public static function prepareStrangeData($data): array;
}

class BookShop implements StrangeDataInterface
{
    public static function prepareStrangeData($data): array
    {
        $preparedData = [];

        foreach ($data as $key => $value) {
            if (preg_match('~^book_[0-9]+$~', $key)) {
                $preparedData['books'][] = ['title' => $value];
            } else {
                $preparedData[$key] = $value;
            }
        }

        return $preparedData;
    }

    // .... other code hidden
}

function makeSerializer(): Symfony\Component\Serializer\Serializer
{
    $extractor = new ReflectionExtractor();

    $nameConverter = new CamelCaseToSnakeCaseNameConverter();

    $arrayDenormalizer = new ArrayDenormalizer(); // seems to help respect the 'adder' typehints in the model. eg `addEmployee(Employee $employee)`

    $strangeDataDenormalizer = new StrangeDataDenormalizer(
        null,
        $nameConverter,
        null,
        $extractor
    );

    $objectNormalizer = new ObjectNormalizer(
        null,
        $nameConverter,
        null,
        $extractor
    );

    $encoder = new JsonEncoder();

    $serializer = new Symfony\Component\Serializer\Serializer(
        [
            $strangeDataDenormalizer,
            $objectNormalizer,
            $arrayDenormalizer,
        ],
        [$encoder]
    );

    return $serializer;
}