如何通过Symfony Serializer自定义生成符合Atom结构的XML文件
实现方法
完全不需要自定义XmlEncoder,靠Symfony Serializer原生的数组映射规则+序列化上下文参数,就能100%自定义任意XML结构,不用改动编码器源码。
你当前代码的几个可修正的问题
- 初始化XmlEncoder时传入的根节点名
result和序列化上下文里的xml_root_node_name参数重复,上下文参数优先级更高,初始化时的参数可以省略 - 你之前定义的带
@xmlns、#的$tag数组完全没有被传入序列化流程,是无效代码 - 现有默认输出和目标结构的差异,本质都是数据结构和渲染规则不匹配,不需要改编码器就能解决:
- 根节点
<feed>需要带命名空间属性 - 根节点下需要加固定的
<logo>节点 - 原有的
<item>标签替换为<entry>,去掉自动生成的key="0"这类冗余索引属性 - 原有的多层嵌套节点(比如
header > title、module > date > time)拆平到<entry>的直接子节点,同时把subtitle改名为notsubtitle、时间节点改名为created - 原有的
body.html内容放到带type="xhtml"属性的<content>节点下,嵌套<div>标签包裹 - 不需要的
location等字段直接过滤掉即可
- 根节点
具体实现代码
public function servicesXml(ContentRepository $content): Response { // 实际项目中直接使用框架自动注入的Serializer服务即可,无需手动实例化 $encoder = [new XmlEncoder()]; $normalizer = [new ObjectNormalizer()]; $serializer = new Serializer($normalizer, $encoder); $contents = $content->getContents(); // 先把数据库查询结果整理成和目标XML完全一一对应的关联数组 $feedData = [ // @开头的key会被识别为标签属性 '@xmlns' => 'http://www.w3.org/2005/Atom', // 固定的logo节点值替换成实际地址 'logo' => 'https://你的域名/logo.png', // 把每条内容映射为entry节点 'entry' => array_map(function(Content $item) { return [ 'id' => $item->getId(), 'title' => $item->getHeader()->getTitle(), 'notsubtitle' => $item->getHeader()->getSubtitle(), 'created' => $item->getHeader()->getModule()->getDate()->getTime(), 'author' => [ 'name' => $item->getHeader()->getAuthor()->getName() ], 'content' => [ // content节点的属性 '@type' => 'xhtml', // 嵌套div包裹html内容,编码器会自动处理特殊字符转义/CDATA 'div' => $item->getBody()->getHtml() ] ]; }, $contents) ]; $xmlOutput = $serializer->serialize($feedData, 'xml', [ 'xml_format_output' => true, 'xml_encoding' => 'utf-8', 'xml_root_node_name' => 'feed', // 去掉数组索引自动生成的key属性,消除类似<item key="0">的冗余输出 'xml_keep_array_node' => false, 'remove_empty_tags' => true ]); return new Response($xmlOutput, 200, ['Content-Type' => 'application/xml']); }
关键的原生支持规则(不用改编码器就能用)
很多开发者遇到自定义结构需求第一反应是重写编码器方法,其实完全没必要——XmlEncoder对关联数组的特殊语法支持已经覆盖了99%的自定义XML结构场景,自定义编码器只适合需要做XML校验、自定义节点逻辑的极端场景。常用的原生规则包括:
- 数组中以
@开头的key,会自动渲染为所在标签的属性,比如'@type' => 'xhtml'最终会生成<content type="xhtml"> - 数组中以
#开头的key,会作为所在标签的纯文本值,XmlEncoder会自动处理特殊字符、CDATA转义,不需要手动拼接标签 - 不需要输出的字段,只要在数组映射环节不加入即可,比在Normalizer里配置忽略属性灵活很多
注意:你目标结构里写的
xlns是拼写笔误,W3C XML规范里命名空间属性是xmlns,拼写错误会导致命名空间失效。
内容的提问来源于stack exchange,提问作者Menryck
相关产品推荐
相关产品推荐

