Java Jackson:如何将随机XML反序列化为Map<String,String>值并原样序列化
问题描述
需要将<Wrapper>元素下<a>元素的所有子元素读取为Wrapper类中myMap的key为"a"的字符串值,测试中遇到两类问题:
- Test 1:使用含嵌套元素的XML字符串,Jackson反序列化后
myMap中key"a"的值为空,序列化输出<Wrapper><a></a></Wrapper>,不符合预期。 - Test 2:将
<a>的子元素用CDATA包裹,反序列化能正确获取字符串值,但序列化后XML标签被转义,输出为<Wrapper><a><b><c>3 &amp; 7 - 8 &lt; 9</c> </b></a></Wrapper>,而预期应为<Wrapper><a><b><c>3 & 7 - 8 < 9</c> </b></a></Wrapper>。
需求:实现将任意XML反序列化为Map<String,String>的字符串值,并原样序列化回初始XML格式。
解决方案
要实现这个需求,需要自定义Jackson的XML序列化/反序列化逻辑,让Map的value直接读取嵌套XML内容为字符串,且序列化时不转义标签。
1. 自定义XML反序列化器(嵌套XML转字符串)
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser; import org.xml.sax.InputSource; import java.io.StringWriter; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamResult; public class XmlStringDeserializer extends StdDeserializer<String> { protected XmlStringDeserializer() { super(String.class); } @Override public String deserialize(JsonParser parser, DeserializationContext ctx) throws IOException { FromXmlParser xmlParser = (FromXmlParser) parser; try { Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); StringWriter writer = new StringWriter(); transformer.transform(new SAXSource(xmlParser.getStaxReader(), new InputSource()), new StreamResult(writer)); return writer.toString().trim(); } catch (Exception e) { throw new IOException("Failed to parse nested XML to string", e); } } }
2. 自定义XML序列化器(字符串原样输出XML标签)
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.std.StdSerializer; import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator; public class XmlStringSerializer extends StdSerializer<String> { protected XmlStringSerializer() { super(String.class); } @Override public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException { ToXmlGenerator xmlGen = (ToXmlGenerator) gen; xmlGen.writeRawValue(value); } }
3. 配置Wrapper类并绑定序列化/反序列化器
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import java.util.Map; @JacksonXmlRootElement(localName = "Wrapper") public class Wrapper { private Map<String, @JsonDeserialize(using = XmlStringDeserializer.class) @JsonSerialize(using = XmlStringSerializer.class) String> myMap; // Getter & Setter public Map<String, String> getMyMap() { return myMap; } public void setMyMap(Map<String, String> myMap) { this.myMap = myMap; } }
4. 测试验证
import com.fasterxml.jackson.dataformat.xml.XmlMapper; public class XmlTest { public static void main(String[] args) throws Exception { XmlMapper xmlMapper = new XmlMapper(); String inputXml = "<Wrapper><a><b><c>3 & 7 - 8 < 9</c> </b></a></Wrapper>"; // 反序列化 Wrapper wrapper = xmlMapper.readValue(inputXml, Wrapper.class); System.out.println("反序列化后myMap['a']的值:" + wrapper.getMyMap().get("a")); // 序列化回XML String outputXml = xmlMapper.writeValueAsString(wrapper); System.out.println("序列化输出XML:" + outputXml); } }
核心说明
- 反序列化器通过JAXP Transformer将嵌套XML节点完整转换为字符串,保留所有标签和实体。
- 序列化器调用
writeRawValue直接输出字符串内容,避免Jackson自动转义XML标签。 - 使用默认
XmlMapper配置即可,无需额外开启转义或特殊处理。
内容的提问来源于stack exchange,提问作者Bằng Rikimaru
相关产品推荐
相关产品推荐

