添加了一个 ConstraintViolationListNormalizer

Contributed by
Grégoire Pineau
in #22150.

用 Symfony 操作 APIs 时,下面的代码很常见:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
 * @Route("/blog/new", name="api_blog_new")
 * @Method("POST")
 * @Security("is_granted('ROLE_ADMIN')")
 */
public function new(Request $request, SerializerInterface $serializer, ValidatorInterface $validator)
{
    $data = $request->getContent();
    $post = $serializer->deserialize($data, Post::class, 'json', ['groups' => ['post_write']]);
    $post->setAuthor($this->getUser());
 
    $violations = $validator->validate($post);
    if (count($violations) > 0) {
        $repr = $serializer->serialize($violations, 'json');
 
        return JsonResponse::fromJsonString($repr, 400);
    }
 
    // ...
}

$violations 变量包括了一个 ConstraintViolationList 对象,它常被转换成一个错误列表,再把列表序列化成一个包含 error的 JSON 响应。这就是为何在 Symfony 4.1 中我们添加了一个 ConstraintViolationListNormalizer 来帮你自动处理的原因。normalizer遵循 RFC 7807 协议来生成错误列表。

把 XML 和 CSV 结果获取为collection

Contributed by
Hamza Amrouche
in #25218 and #25369.

CsvEncoderXmlEncoder 现在定义了一个新的名为 as_collection 的配置选项。如果把此选项作为上下文的一部分传入参数,并把它设为 true,则结果就会是一个结果集。

denormalization时默认的构造器参数

Contributed by
Maxime Veber
in #25493.

如果一个类的构造器中定义了参数,一般在使用一个 Value对象 时,serializer 无法创建这个对象。在 Symfony 4.1 中,我们引入了全新的 default_constructor_arguments 上下文选项,来解决此问题。

下例中, foobar 都需要构造器参数,但只有 foo 提供了。 bar 则是由 default_constructor_arguments 选项获取到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
 
class MyObj
{
    private $foo;
    private $bar;
 
    public function __construct($foo, $bar)
    {
        $this->foo = $foo;
        $this->bar = $bar;
    }
}
 
$normalizer = new ObjectNormalizer($classMetadataFactory);
$serializer = new Serializer(array($normalizer));
 
// this is equivalent to $data = new MyObj('Hello', '');
// 这等同于 $data = new MyObj('Hello', '');
$data = $serializer->denormalize(['foo' => 'Hello'], 'MyObj', [
    'default_constructor_arguments' => [
        'MyObj' => ['foo' => '', 'bar' => ''],
    ]
]);

添加了一个 MaxDepth handler

Contributed by
Kévin Dunglas
in #26108.

在达到所配置的最大深度时,有时并不需要停止序列化的进程 ,更好的方式是让开发者处理这种情况,返回一些东西 (如,entity的识别符)。

在 Symfony 4.1 中你可以解决这个问题,用全新的 setMaxDepthHandler() 方法自定义一个 handler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
use Doctrine\Common\Annotations\AnnotationReader;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Annotation\MaxDepth;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
 
class Foo
{
    public $id;
 
    /** @MaxDepth(1) */
    public $child;
}
 
$level1 = new Foo();
$level1->id = 1;
 
$level2 = new Foo();
$level2->id = 2;
$level1->child = $level2;
 
$level3 = new Foo();
$level3->id = 3;
$level2->child = $level3;
 
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$normalizer = new ObjectNormalizer($classMetadataFactory);
$normalizer->setMaxDepthHandler(function ($foo) {
    return '/foos/'.$foo->id;
});
 
$serializer = new Serializer(array($normalizer));
$result = $serializer->normalize($level1, null, array(ObjectNormalizer::ENABLE_MAX_DEPTH => true));
/*
$result = array[
    'id' => 1,
    'child' => [
        'id' => 2,
        'child' => '/foos/3',
    ]
];
*/

在 decoding XML 时忽略注释

Contributed by
James Sansbury
in #26445.

在之前的 Symfony 版本中,XML 组件在 decoding 内容时被调用。同时,如果 XML 的第一行是注释,它会被视为反解出的 XML 的根结点。

在 Symfony 4.1 中,XML 注释默认被移除了,但你可以用新的、可选的第三个参数来控制这种行为:

1
2
3
4
5
6
7
8
9
10
class XmlEncoder
{
    public function __construct(
        string $rootNodeName = 'response',
        int $loadOptions = null,
        array $ignoredNodeTypes = array(XML_PI_NODE, XML_COMMENT_NODE)
    ) {
        // ...
    }
}