让 Jackson XMLMapper 读取根元素名称
Get Jackson XMLMapper to read root element name
如何让 Jackson 的 XMLMapper 在反序列化时读取根 xml 元素的名称?
我正在将输入 XML 反序列化为通用 Java class、LinkedHashMap,然后反序列化为 JSON。我想在反序列化到 LinkedHashMap 时动态读取输入 XML 的根元素。
代码
XmlMapper xmlMapper = new XmlMapper();
Map entries = xmlMapper.readValue(new File("source.xml"), LinkedHashMap.class);
ObjectMapper jsonMapper = new ObjectMapper();
String json = jsonMapper.writer().writeValueAsString(entries);
System.out.println(json);
输入XML
<?xml version="1.0" encoding="ISO-8859-1"?>
<File>
<NumLeases>1</NumLeases>
<NEDOCO>18738</NEDOCO>
<NWUNIT>0004</NWUNIT>
<FLAG>SUCCESS</FLAG>
<MESSAGE>Test Upload</MESSAGE>
<Lease>
<LeaseVersion>1</LeaseVersion>
<F1501B>
<NEDOCO>18738</NEDOCO>
<NWUNIT>0004</NWUNIT>
<NTRUSTRECORDKEY>12</NTRUSTRECORDKEY>
</F1501B>
</Lease>
</File>
实际输出
{"NumLeases":"1","NEDOCO":"18738","NWUNIT":"0004","FLAG":"SUCCESS","MESSAGE":"Test Upload","Lease":{"LeaseVersion":"1","F1501B":{"NEDOCO":"18738","NWUNIT":"0004","NTRUSTRECORDKEY":"12"}}}
预期输出(注意:JSON 中存在一个名为 "File" 的根元素)
{"File":{"NumLeases":"1","NEDOCO":"18738","NWUNIT":"0004","FLAG":"SUCCESS","MESSAGE":"Test Upload","Lease":{"LeaseVersion":"1","F1501B":{"NEDOCO":"18738","NWUNIT":"0004","NTRUSTRECORDKEY":"12"}}}}
可能在某处设置了一些开关。任何帮助都将不胜感激。
遗憾的是没有标志。它可以通过 com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer
的自定义实现来完成。 (Jackson How-To: Custom Deserializers):
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser;
import java.io.File;
import java.io.IOException;
//...
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.registerModule(new SimpleModule().addDeserializer(JsonNode.class,
new JsonNodeDeserializer() {
@Override
public JsonNode deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String rootName = ((FromXmlParser)p).getStaxReader().getLocalName();
return ctxt.getNodeFactory()
.objectNode().set(rootName, super.deserialize(p, ctxt));
}
}));
JsonNode entries = xmlMapper.readTree(new File("source.xml"));
System.out.println(entries);
虽然这个问题有一个可接受的答案,但我发现它不适用于最新的 Jackson 版本 2.13.2 并且无论如何使用有缺陷的方法。
new SimpleModule().addDeserializer(JsonNode.class,
new JsonNodeDeserializer() {
@Override
public JsonNode deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String rootName = ((FromXmlParser)p).getStaxReader().getLocalName();
return ctxt.getNodeFactory()
.objectNode().set(rootName, super.deserialize(p, ctxt));
}
}));
.getLocalName()
调用将 return 第一个 child 元素的名称,而不是解析输入的实际根。此外,仅获取元素的 name 会忽略属性,因此您最终会在输出中得到一个重复的标签名称。
该怎么做?
在尝试了多种变通方法后,我发现只有一种可以正常工作。你必须让 Jackson 移除它的根节点并用一个虚拟包装标签来欺骗它。
JsonNode jsonNode = XML_MAPPER.readTree("<tag>" + nestedXmlString + "</tag>");
这将用一个虚拟 <tag>
包裹 XML,然后立即将其删除并遗忘。
然后,您可以照常使用输出树:
toXmlGenerator.writeTree(jsonNode);
注意
但是,请注意,如果您的 XML 输入字符串包含 XML Header 声明 (<?xml...
),则用虚拟标签包装它会导致在解析异常中。为避免这种情况,您必须先从输入中删除声明字符串:
String nestedXmlString = input;
if (nestedXmlString.startsWith("<?xml")) {
nestedXmlString = nestedXmlString.substring(nestedXmlString.indexOf("?>") + 2);
}
如何让 Jackson 的 XMLMapper 在反序列化时读取根 xml 元素的名称?
我正在将输入 XML 反序列化为通用 Java class、LinkedHashMap,然后反序列化为 JSON。我想在反序列化到 LinkedHashMap 时动态读取输入 XML 的根元素。
代码
XmlMapper xmlMapper = new XmlMapper();
Map entries = xmlMapper.readValue(new File("source.xml"), LinkedHashMap.class);
ObjectMapper jsonMapper = new ObjectMapper();
String json = jsonMapper.writer().writeValueAsString(entries);
System.out.println(json);
输入XML
<?xml version="1.0" encoding="ISO-8859-1"?>
<File>
<NumLeases>1</NumLeases>
<NEDOCO>18738</NEDOCO>
<NWUNIT>0004</NWUNIT>
<FLAG>SUCCESS</FLAG>
<MESSAGE>Test Upload</MESSAGE>
<Lease>
<LeaseVersion>1</LeaseVersion>
<F1501B>
<NEDOCO>18738</NEDOCO>
<NWUNIT>0004</NWUNIT>
<NTRUSTRECORDKEY>12</NTRUSTRECORDKEY>
</F1501B>
</Lease>
</File>
实际输出
{"NumLeases":"1","NEDOCO":"18738","NWUNIT":"0004","FLAG":"SUCCESS","MESSAGE":"Test Upload","Lease":{"LeaseVersion":"1","F1501B":{"NEDOCO":"18738","NWUNIT":"0004","NTRUSTRECORDKEY":"12"}}}
预期输出(注意:JSON 中存在一个名为 "File" 的根元素)
{"File":{"NumLeases":"1","NEDOCO":"18738","NWUNIT":"0004","FLAG":"SUCCESS","MESSAGE":"Test Upload","Lease":{"LeaseVersion":"1","F1501B":{"NEDOCO":"18738","NWUNIT":"0004","NTRUSTRECORDKEY":"12"}}}}
可能在某处设置了一些开关。任何帮助都将不胜感激。
遗憾的是没有标志。它可以通过 com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer
的自定义实现来完成。 (Jackson How-To: Custom Deserializers):
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser;
import java.io.File;
import java.io.IOException;
//...
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.registerModule(new SimpleModule().addDeserializer(JsonNode.class,
new JsonNodeDeserializer() {
@Override
public JsonNode deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String rootName = ((FromXmlParser)p).getStaxReader().getLocalName();
return ctxt.getNodeFactory()
.objectNode().set(rootName, super.deserialize(p, ctxt));
}
}));
JsonNode entries = xmlMapper.readTree(new File("source.xml"));
System.out.println(entries);
虽然这个问题有一个可接受的答案,但我发现它不适用于最新的 Jackson 版本 2.13.2 并且无论如何使用有缺陷的方法。
new SimpleModule().addDeserializer(JsonNode.class,
new JsonNodeDeserializer() {
@Override
public JsonNode deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String rootName = ((FromXmlParser)p).getStaxReader().getLocalName();
return ctxt.getNodeFactory()
.objectNode().set(rootName, super.deserialize(p, ctxt));
}
}));
.getLocalName()
调用将 return 第一个 child 元素的名称,而不是解析输入的实际根。此外,仅获取元素的 name 会忽略属性,因此您最终会在输出中得到一个重复的标签名称。
该怎么做?
在尝试了多种变通方法后,我发现只有一种可以正常工作。你必须让 Jackson 移除它的根节点并用一个虚拟包装标签来欺骗它。
JsonNode jsonNode = XML_MAPPER.readTree("<tag>" + nestedXmlString + "</tag>");
这将用一个虚拟 <tag>
包裹 XML,然后立即将其删除并遗忘。
然后,您可以照常使用输出树:
toXmlGenerator.writeTree(jsonNode);
注意
但是,请注意,如果您的 XML 输入字符串包含 XML Header 声明 (<?xml...
),则用虚拟标签包装它会导致在解析异常中。为避免这种情况,您必须先从输入中删除声明字符串:
String nestedXmlString = input;
if (nestedXmlString.startsWith("<?xml")) {
nestedXmlString = nestedXmlString.substring(nestedXmlString.indexOf("?>") + 2);
}