如何在Symfony中用FOS/JMS BodyConverter从单负载反序列化多对象?
单个请求负载反序列化多对象(FOS/JMS BodyConverter + Symfony)
需求场景
需要从单个请求负载中反序列化出两个对象:
{ "object_data_1": ..., "object_data_2" : ...}
并通过ParamConverter注解直接在控制器方法中获取两个对象实例:
#[ParamConverter('obj1', options: ['configure starting node here?'], converter: 'fos_rest.request_body')] #[ParamConverter('obj2', options: ['configure starting node here?'], converter: 'fos_rest.request_body')] public function controllerMethod(Obj1 $obj1, Obj2 $obj2) : Response {...}
可行实现方案
1. 基于JMS序列化组的基础方案
利用JMS序列化器的分组特性,匹配请求中的指定字段:
- 给实体类的属性添加序列化别名和分组标记:
use JMS\Serializer\Annotation as Serializer; class Obj1 { /** * @Serializer\SerializedName("object_data_1") * @Serializer\Groups({"obj1"}) */ private $property1; // 其他属性、getter/setter }
class Obj2 { /** * @Serializer\SerializedName("object_data_2") * @Serializer\Groups({"obj2"}) */ private $propertyA; // 其他属性、getter/setter }
- 控制器中通过
deserialization_context指定分组:
#[ParamConverter('obj1', options: [ 'deserialization_context' => ['groups' => ['obj1']] ], converter: 'fos_rest.request_body')] #[ParamConverter('obj2', options: [ 'deserialization_context' => ['groups' => ['obj2']] ], converter: 'fos_rest.request_body')] public function controllerMethod(Obj1 $obj1, Obj2 $obj2): Response { // 业务逻辑 }
2. 自定义ParamConverter(灵活方案)
如果分组方式不满足需求,可自定义转换器直接指定解析节点:
- 创建自定义转换器类:
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterInterface; use Symfony\Component\HttpFoundation\Request; use JMS\Serializer\SerializerInterface; class MultiObjectParamConverter implements ParamConverterInterface { private $serializer; public function __construct(SerializerInterface $serializer) { $this->serializer = $serializer; } public function apply(Request $request, ParamConverter $configuration) { $rawData = json_decode($request->getContent(), true); $paramName = $configuration->getName(); $targetClass = $configuration->getClass(); $targetNode = $configuration->getOptions()['node'] ?? ''; if (!isset($rawData[$targetNode])) { return false; } $object = $this->serializer->deserialize( json_encode($rawData[$targetNode]), $targetClass, 'json' ); $request->attributes->set($paramName, $object); return true; } public function supports(ParamConverter $configuration) { return $configuration->getConverter() === 'multi_object'; } }
- 在
services.yaml中注册转换器:
services: App\Request\ParamConverter\MultiObjectParamConverter: tags: - { name: sensio_framework_extra.param_converter }
- 控制器中使用自定义转换器:
#[ParamConverter('obj1', options: [ 'node' => 'object_data_1' ], converter: 'multi_object')] #[ParamConverter('obj2', options: [ 'node' => 'object_data_2' ], converter: 'multi_object')] public function controllerMethod(Obj1 $obj1, Obj2 $obj2): Response { // 业务逻辑 }
3. 手动解析请求体(快速方案)
如果不想改动配置,可直接在控制器中手动拆分反序列化:
use JMS\Serializer\SerializerInterface; public function controllerMethod(Request $request, SerializerInterface $serializer): Response { $rawData = json_decode($request->getContent(), true); $obj1 = $serializer->deserialize( json_encode($rawData['object_data_1']), Obj1::class, 'json' ); $obj2 = $serializer->deserialize( json_encode($rawData['object_data_2']), Obj2::class, 'json' ); // 业务逻辑 }
内容的提问来源于stack exchange,提问作者Friedrich Roell
相关产品推荐
相关产品推荐

