You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:24:42