Php 序列化程序Symfony上的回调
我正在运行symfony2.7,并尝试将一个对象(条令实体)输出为JSON 当我规范化对象时,我想转换它的一些值。为此,我在中找到了“setCallbacks”方法,但我有点困惑于如何将其应用到我的案例中 在调用Symfonys serializer服务时,是否有方法调用规范化器上设置的“setCallbacks”方法 下面是一个简短的例子,说明我正在努力实现的目标:Php 序列化程序Symfony上的回调,php,symfony,serialization,Php,Symfony,Serialization,我正在运行symfony2.7,并尝试将一个对象(条令实体)输出为JSON 当我规范化对象时,我想转换它的一些值。为此,我在中找到了“setCallbacks”方法,但我有点困惑于如何将其应用到我的案例中 在调用Symfonys serializer服务时,是否有方法调用规范化器上设置的“setCallbacks”方法 下面是一个简短的例子,说明我正在努力实现的目标: //ExampleController.php public function getJSONOrderByIdAction(
//ExampleController.php
public function getJSONOrderByIdAction($id) {
$serializer = $this->get('serializer');
$normalizer = $serializer->getNormalizer(); // <- This is what I'm unable to do
$dateTimeToString = function ($dateTime) {
return $dateTime instanceof \DateTime ? $dateTime->format(\DateTime::ISO8601) : '';
};
$normalizer->setCallbacks(['time' => $dateTimeToString]);
$order = $this->getDoctrine()->find("AppBundle:Order", $id);
return new JsonResponse(["order" => $serializer->normalize($order, null, ["groups" => ["public"]])]);
}
//ExampleController.php
公共函数getJSONOrderByIdAction($id){
$serializer=$this->get('serializer');
$normalizer=$serializer->getNormalizer();//在我看来,您似乎试图让事情变得过于复杂。以下是我在需要将实体序列化为JSON时采取的方法:
PHP2.5及以上版本允许您在对象上实现该方法,只需直接调用对象即可
如果您仍在使用PHP2.4,只需手动调用对象上的jsonSerialize()
例如:
/**
* @ORM\Entity
*/
class MyEntity {
...
public function jsonSerialize() {
$data = array("foo" => $this->bar());
// add other data here ...
return $data
}
}
然后在调用代码中:
// for PHP 2.5 and up:
$normalized = json_encode($myEntityInstance);
// for PHP 2.4 and below
$normalized = json_encode($myEntityInstance->jsonSerialize());
默认序列化程序服务是在依赖项注入阶段创建的,序列化程序接口不允许编辑(完全)检索规范化程序
我认为你(至少)有三个选择:
将自定义规范化程序添加到默认序列化程序服务
将NormalizableInterface添加到实体中
按照您的尝试创建一个新的序列化程序服务(或文档建议的本地对象)
我认为在您的场景中,案例1是首选的(因为案例2很快就会变得无聊)
我将这样做;首先创建一个自定义规范化器
<?php
namespace AppBundle;
class DateTimeNormalizer extends SerializerAwareNormalizer implements NormalizerInterface, DenormalizerInterface
{
/**
* {@inheritdoc}
*/
public function normalize($object, $format = null, array $context = array())
{
return $object->format(\DateTime::ISO8601);
}
/**
* {@inheritdoc}
*/
public function denormalize($data, $class, $format = null, array $context = array())
{
return new $class($data);
}
/**
* Checks if the given class is a DateTime.
*
* @param mixed $data Data to normalize.
* @param string $format The format being (de-)serialized from or into.
*
* @return bool
*/
public function supportsNormalization($data, $format = null)
{
return $data instanceof \DateTime;
}
/**
* Checks if the given class is a DateTime.
*
* @param mixed $data Data to denormalize from.
* @param string $type The class to which the data should be denormalized.
* @param string $format The format being deserialized from.
*
* @return bool
*/
public function supportsDenormalization($data, $type, $format = null)
{
$class = new \ReflectionClass($type);
return $class->isSubclassOf('\DateTime');
}
}
我自己的解决方案
按照giosh94mhz的建议,我尝试切换到JMS序列化程序,但最终返回到Symfonys序列化程序
JMS序列化程序介绍了它自己的问题,在为我偶然发现的问题寻找答案时,我发现了一个非常出色的工具,它解释了如何在Symfony中使用和实现您自己的规范化程序。您可以使用K.Dunglas组件的回调规范化程序
您可以在ObjectNormalizer(在normalize方法中)中看到这一点
这意味着您必须在回调数组键中使用不希望规范化的属性的名称
例如,在我的实体中,我有一个名为“name”的字段,类型为“pgarray”(类似于postgresql的数组)。我不想规范化此数据。相反,我想要一个字符串
/**
* $object represent the property "name" because callback is attached to name property (setCallback)
*/
$nameCallback = function ($object, $outerObject = null) {
return $object[0];
};
$this->normalizer->setCallbacks(['name' => $dateCallback]);
请记住,由于Symfony 4.2,您必须在DI中使用$context才能使用回调。我更愿意让实体尽可能模块化。当前控制器操作必须以某种方式转换对象,这在其他情况下是不需要的。哦,我明白了。是的,您不希望用非标准序列化来混乱实体类ions。我还没有测试过代码,但应该可以。你自己试试,如果需要的话给我一些反馈;我会为社区更新答案。这看起来可能是一个不错的选择。DateTime示例正是他们在Symfonys文档中使用的示例-但假设我的订单有一系列产品,我需要对它们做些什么这也是。我是要创建一个ProductNormalizer,还是需要以某种方式处理集合?另外,我也不完全确定如何将新的规范化器应用于Symfony服务?我认为仅仅创建服务是不够的,序列化器似乎没有“addNormalizer”方法。如果其中一些方法是给定的,我很抱歉。这对我来说是一个全新的领域。@Daniel默认的规范化过程最好是提供一个默认的实现,但默认值通常会转化为丑陋的。没有“addNormalizer”方法(出于某些很好的原因),但标记“serializer.normalizer”是供您使用的。您可以有一个处理多个类的通用MyTweeksToNormalizer
类,但这可能会变得很复杂。或者,您可以对所有实体实现NormalizableInterface
。作为更好的解决方案,您可以切换到jms serializer
以及配置驱动序列化的好处。将NormalizableInterface应用于所有实体不是一个选项,因为我需要能够以不同的方式对同一对象进行规范化。我开始认为JMS序列化程序确实是一种可行的方法。感谢您提供的详细答案。@Daniel an“in object”总是快速简单,但灵活性较低。JMS序列化程序具有“序列化上下文”。通过使用不同的上下文,您可以在不同的情况下选择序列化的方式和内容;所有这些都归结为编写$serizalizer->serialize($object,'my context')
。无论如何,不客气!只要记住+1或接受它就行了;)
if (isset($this->callbacks[$attribute])) {
$attributeValue = call_user_func($this->callbacks[$attribute], $attributeValue);
}
/**
* $object represent the property "name" because callback is attached to name property (setCallback)
*/
$nameCallback = function ($object, $outerObject = null) {
return $object[0];
};
$this->normalizer->setCallbacks(['name' => $dateCallback]);