Symfony Serializer:非规范化(反序列化)笨拙的数组数据
Symfony Serializer: Denormalize (Deserialize) awkward array data
使用 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
}
当使用下面的 "cleaner" JSON 时,一切都按预期工作,我得到一个 BookShop
和 Book
数组返回。
{
"name": "Steves Book Shop",
"books": [
{ "title": "Lord of the Rings" },
{ "title": "The Hobbit" }
]
}
什么是对原始 JSON 进行反规范化的干净方法,它反而具有烦人的 book_1
、book_2
等
我一直在试验自定义反规范化器 (DenormalizerInterface
),我的解决方案看起来比您预期的要难得多。
您应该为这些书籍使用 ArrayCollection - 假设它们只是您应用程序中的另一个实体
这是我最终得到的解决方案,我不完全相信这是最好的方法,但它目前有效。欢迎任何反馈:
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;
}
使用 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
}
当使用下面的 "cleaner" JSON 时,一切都按预期工作,我得到一个 BookShop
和 Book
数组返回。
{
"name": "Steves Book Shop",
"books": [
{ "title": "Lord of the Rings" },
{ "title": "The Hobbit" }
]
}
什么是对原始 JSON 进行反规范化的干净方法,它反而具有烦人的 book_1
、book_2
等
我一直在试验自定义反规范化器 (DenormalizerInterface
),我的解决方案看起来比您预期的要难得多。
您应该为这些书籍使用 ArrayCollection - 假设它们只是您应用程序中的另一个实体
这是我最终得到的解决方案,我不完全相信这是最好的方法,但它目前有效。欢迎任何反馈:
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;
}