如何让XmlMapper保留原始XML根元素?
问题
我有一段用于解析并美化XML字符串的Java代码:
final var xmlString = "<parent><child>data</child></parent>"; final var xmlMapper = XmlMapper.xmlBuilder() .enable(SerializationFeature.INDENT_OUTPUT) .build(); final var tree = xmlMapper.reader().readTree(xmlString); final var formattedXmlString = xmlMapper.writeValueAsString(tree); System.out.println(formattedXmlString);
我的需求是解析任意XML字符串并美化输出用于日志,但当前代码会把原始根节点替换成<ObjectNode>,输出如下:
<ObjectNode> <child>data</child> </ObjectNode>
我期望保留原始根节点的美化输出:
<parent> <child>data</child> </parent>
由于输入XML内容不固定,无法提前定义POJO类,也试过包装和解包操作但没成功。我想避免手动解析根元素名或正则替换的方式(虽然下面的临时方案能运行,但并不合理):
final var tree = xmlMapper.reader().readTree("<_>" + xmlString + "</_>"); final var formattedXmlString = xmlMapper .writeValueAsString(tree) .replaceFirst("^<ObjectNode>\n", "") .replaceAll("</ObjectNode>$", "") .replaceAll(" {2}(.*)", "$1");
解决方案
可以通过Jackson XML模块的解析器先获取原始XML的根元素名称,再在序列化JsonNode时指定该名称作为根节点,无需正则或POJO:
final var xmlString = "<parent><child>data</child></parent>"; final var xmlMapper = XmlMapper.xmlBuilder() .enable(SerializationFeature.INDENT_OUTPUT) .build(); // 读取XML为JsonNode final var tree = xmlMapper.reader().readTree(xmlString); // 通过XmlParser获取原始根元素名称 final XmlFactory xmlFactory = xmlMapper.getFactory(); try (XmlParser parser = xmlFactory.createParser(xmlString)) { parser.nextToken(); // 跳过文档开始标记 parser.nextToken(); // 定位到根元素的起始标签 String rootName = parser.getCurrentName(); // 序列化时指定根节点名称为原始名称 final var formattedXmlString = xmlMapper.writer().withRootName(rootName).writeValueAsString(tree); System.out.println(formattedXmlString); } catch (IOException e) { e.printStackTrace(); }
说明
- 原代码问题在于:
XmlMapper继承自ObjectMapper,默认会将XML根元素的内容解析为ObjectNode,序列化时自动用<ObjectNode>作为根节点,丢失了原始根元素名称。 - 上述方案通过
XmlParser直接读取XML的根元素名称,再通过writer().withRootName(rootName)强制指定序列化的根节点名称,完美保留原始XML结构的同时完成美化。
内容的提问来源于stack exchange,提问作者Nikolas
相关产品推荐
相关产品推荐

