如何使用JAXB合并多个XML文件?附当前代码及异常输出
问题描述
这是我第一次使用Stack Overflow,我的英语不太好,请多多包涵。我采用如下追加模式使用JAXB:
for (Document330 document : documents){ JAXBContext jContext = JAXBContext.newInstance(Document330Xml.class); Marshaller m = jContext.createMarshaller(); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); m.marshal(document, fos); }
但生成的输出文件存在重复XML声明及多根节点问题,示例内容如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <DOCUMENT>... <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <DOCUMENT>...
解决方法
嗨,别担心,英语不好完全没问题~你遇到的问题其实很好解决:每次循环都创建新的JAXBContext和Marshaller,而且直接把每个Document330对象追加到同一个输出流里,就会导致每次marshal都自动写入一份XML声明,同时多个<DOCUMENT>节点并列,违反了XML必须只有一个根节点的规则。
给你两种靠谱的解决思路:
方法一:用包装类统一生成(推荐)
这种方式符合JAXB的设计规范,代码更整洁易维护。我们只需要给所有<DOCUMENT>节点套一个统一的根节点,比如<DOCUMENTS>,然后一次性完成marshal操作。
步骤1:创建包装类
先写一个用来包裹所有文档的实体类:
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import java.util.List; @XmlRootElement(name = "DOCUMENTS") public class DocumentsWrapper { private List<Document330> documents; @XmlElement(name = "DOCUMENT") public List<Document330> getDocuments() { return documents; } public void setDocuments(List<Document330> documents) { this.documents = documents; } }
步骤2:修改你的业务代码
把JAXBContext和Marshaller的创建移到循环外面(因为JAXBContext创建成本很高,重复创建纯纯浪费资源),然后用包装类一次性输出:
// 只初始化一次JAXB相关实例,放在循环外 JAXBContext jContext = JAXBContext.newInstance(DocumentsWrapper.class); Marshaller m = jContext.createMarshaller(); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); // 把所有文档放进包装类 DocumentsWrapper wrapper = new DocumentsWrapper(); wrapper.setDocuments(documents); // 一次性marshal到输出流,只会生成一份XML声明和单根节点 m.marshal(wrapper, fos);
方法二:手动控制XML结构(无需修改实体类)
如果不想新增包装类,也可以手动写入XML的声明和根节点,同时禁用Marshaller自动生成XML声明,只循环输出每个<DOCUMENT>的内容:
import java.nio.charset.StandardCharsets; JAXBContext jContext = JAXBContext.newInstance(Document330Xml.class); Marshaller m = jContext.createMarshaller(); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); // 禁用Marshaller自动写入XML声明 m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); // 手动写入XML声明和根节点开始标签 fos.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n".getBytes(StandardCharsets.UTF_8)); fos.write("<DOCUMENTS>\n".getBytes(StandardCharsets.UTF_8)); for (Document330 document : documents) { // 只输出<DOCUMENT>节点的内容,不会加XML声明 m.marshal(document, fos); // 加换行让格式更美观 fos.write("\n".getBytes(StandardCharsets.UTF_8)); } // 手动写入根节点结束标签 fos.write("</DOCUMENTS>".getBytes(StandardCharsets.UTF_8));
两种方法对比下来,第一种更推荐,因为它完全利用JAXB的特性,后续维护起来更省心;第二种适合临时快速修改的场景,但要注意编码和格式的细节哦~
内容的提问来源于stack exchange,提问作者Akira
相关产品推荐
相关产品推荐

