未知XML类结构下XML与Object互转问题求助
你遇到的这个问题其实很典型——JAXB根本不支持直接用Object.class来解析结构未知的XML!当你传入Object.class时,JAXB无法正确映射XML的节点层级和内容,生成的对象其实并没有完整保留原始XML的信息,所以后续marshal的时候自然输出不完整甚至截断。
为什么会这样?
JAXB是基于注解的XML-对象绑定框架,它需要明确的类结构(带@XmlRootElement、@XmlElement等注解)来映射XML元素。用Object.class这种通用类,JAXB只能做最基础的解析,没法把XML的节点、属性这些信息都存到对象里,转回XML时当然还原不了完整内容。
两种可行的解决方案
方案1:用DOM树直接处理(推荐给完全未知结构的场景)
如果你的需求只是把XML完整地转成某种中间格式再转回,不用操作对象属性,那直接用DOM来保存整个XML结构是最稳妥的:
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import org.xml.sax.InputSource; import java.io.StringReader; import java.io.StringWriter; public class XmlTest { public static void main(String[] args) throws Exception { String XMLString = "<?xml version='1.0' encoding='UTF-8'?><dataset><height>true</height></dataset>"; // 解析XML到DOM文档 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); Document doc = db.parse(new InputSource(new StringReader(XMLString))); // 将DOM文档转回XML字符串 TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); // 保留XML声明 transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); // 格式化输出(可选) transformer.setOutputProperty(OutputKeys.INDENT, "yes"); StringWriter sw = new StringWriter(); transformer.transform(new DOMSource(doc), new StreamResult(sw)); String xmlString = sw.toString(); System.out.println(xmlString); } }
这个方法会完整保留原始XML的所有结构,包括根节点、子节点、属性甚至注释(如果有的话)。
方案2:用通用JAXB类接收未知结构
如果必须要转成对象来操作,可以定义一个带@XmlAnyElement注解的通用类,用来接收任意子节点:
首先定义通用根类:
import javax.xml.bind.annotation.XmlAnyElement; import java.util.List; public class GenericXmlContainer { private List<Object> childElements; @XmlAnyElement public List<Object> getChildElements() { return childElements; } public void setChildElements(List<Object> childElements) { this.childElements = childElements; } }
然后用这个类来解析和转回:
import javax.xml.bind.JAXB; import java.io.StringReader; import java.io.StringWriter; public class XmlTest { public static void main(String[] args) { String XMLString = "<?xml version='1.0' encoding='UTF-8'?><dataset><height>true</height></dataset>"; // 解析到通用容器对象 GenericXmlContainer xmlObj = JAXB.unmarshal(new StringReader(XMLString), GenericXmlContainer.class); // 转回XML字符串 StringWriter sw = new StringWriter(); JAXB.marshal(xmlObj, sw); String xmlString = sw.toString(); System.out.println(xmlString); } }
注意:这个方式会把根节点下的所有子节点都放到childElements列表里,如果你的XML根节点是固定名称(比如dataset),可以给GenericXmlContainer加上@XmlRootElement(name = "dataset")注解,这样转回的XML会保留正确的根节点名称。
总结
如果是完全未知结构的XML,DOM方式更可靠;如果需要对象化操作,用带@XmlAnyElement的通用类是JAXB框架内的可行方案。别再用Object.class来解析了,JAXB根本吃不下这种无结构的输入~
内容的提问来源于stack exchange,提问作者Garuuk

