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

如何用Java生成含指定字段CDATA的XML(替代已弃用XmlSerializer)

问题描述

我需要更新一段旧代码,该代码使用了Xerces 2.9.0中已弃用的XmlSerializer类,官方建议新应用使用DOM Level 3 LSSerializer或JAXP的XML转换API(TrAX)来序列化XML。需求是从Java类生成XML,并为XML中的特定字段outerHtml添加<![CDATA[前缀和]]>后缀。

以下是旧代码实现:

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;

private static File generateXmlWithCDATA(MyObjectJava myObjectJava, File fileXmlOutput) throws JAXBException, IOException {
    JAXBContext requestJaxbContext = JAXBContext.newInstance(MyObjectJava.class);

    Marshaller marshaller = requestJaxbContext.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
     // get an Apache XMLSerializer configured to generate CDATA
    try(OutputStream out = new FileOutputStream(fileXmlOutput)) {       
        XMLSerializer serializer = getXMLSerializer(out);       
        marshaller.marshal(myObjectJava, serializer.asContentHandler());   
    }
    return fileXmlOutput;
}


private static XMLSerializer getXMLSerializer(OutputStream os) {
    // configure an OutputFormat to handle CDATA
    OutputFormat of = new OutputFormat();

    // specify which of your elements you want to be handled as CDATA.
    // The use of the '^' between the namespaceURI and the localname
    // seems to be an implementation detail of the xerces code.
    // When processing xml that doesn't use namespaces, simply omit the
    // namespace prefix as shown in the third CDataElement below.
    of.setCDataElements(
            new String[] { 
                "^outerHtml"   // <outerHtml>
        });   

    // set any other options you'd like
    of.setPreserveSpace(true);
    of.setIndenting(true);

    // create the serializer
    final XMLSerializer serializer = new XMLSerializer(of);
    serializer.setOutputByteStream(os);

    return serializer;
}

期望生成的XML输出示例:

<MyObjectJava>
  <name>Leo</name>
  <surname>DaVinci</surname>
  <outerHtml><![CDATA[<a href="https://www.isasite.com">Is a site</a>]]></outerHtml>
</MyObjectJava>
解决方案

方案一:使用DOM Level 3 LSSerializer

该方案先将JAXB对象序列化为DOM Document,再遍历DOM树找到目标元素,将其文本内容替换为CDATA节点,最后用LSSerializer输出XML。

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
import org.w3c.dom.ls.LSSerializer;

private static File generateXmlWithCDATA(MyObjectJava myObjectJava, File fileXmlOutput) throws JAXBException, IOException {
    // 初始化JAXB上下文和Marshaller
    JAXBContext jaxbContext = JAXBContext.newInstance(MyObjectJava.class);
    Marshaller marshaller = jaxbContext.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

    // 将JAXB对象序列化为DOM Document
    Document document = marshaller.getNode(myObjectJava).getOwnerDocument();

    // 遍历DOM树,为outerHtml元素添加CDATA
    addCDATAToElement(document, "outerHtml");

    // 使用LSSerializer输出XML到文件
    DOMImplementationLS domImpl = (DOMImplementationLS) document.getImplementation();
    LSSerializer serializer = domImpl.createLSSerializer();
    serializer.getDomConfig().setParameter("format-pretty-print", true);
    serializer.getDomConfig().setParameter("encoding", "UTF-8");

    try (FileOutputStream outputStream = new FileOutputStream(fileXmlOutput)) {
        LSOutput output = domImpl.createLSOutput();
        output.setByteStream(outputStream);
        serializer.write(document, output);
    }

    return fileXmlOutput;
}

private static void addCDATAToElement(Document document, String elementName) {
    NodeList nodeList = document.getElementsByTagName(elementName);
    for (int i = 0; i < nodeList.getLength(); i++) {
        Element element = (Element) nodeList.item(i);
        String textContent = element.getTextContent().trim();
        // 清空元素现有子节点
        while (element.hasChildNodes()) {
            element.removeChild(element.getFirstChild());
        }
        // 创建并添加CDATA节点
        CDATASection cdataSection = document.createCDATASection(textContent);
        element.appendChild(cdataSection);
    }
}

方案二:使用JAXP TrAX API(Transformer)

该方案先将JAXB对象转为DOM Document,处理CDATA后,用Transformer完成XML序列化输出。

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

private static File generateXmlWithCDATA(MyObjectJava myObjectJava, File fileXmlOutput) throws JAXBException, IOException, TransformerException {
    // JAXB初始化并序列化为DOM Document
    JAXBContext jaxbContext = JAXBContext.newInstance(MyObjectJava.class);
    Marshaller marshaller = jaxbContext.createMarshaller();
    Document document = marshaller.getNode(myObjectJava).getOwnerDocument();

    // 为outerHtml添加CDATA
    addCDATAToElement(document, "outerHtml");

    // 使用Transformer输出XML
    TransformerFactory transformerFactory = TransformerFactory.newInstance();
    Transformer transformer = transformerFactory.newTransformer();
    // 设置输出属性:格式化、编码、缩进量
    transformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, "yes");
    transformer.setOutputProperty(javax.xml.transform.OutputKeys.ENCODING, "UTF-8");
    transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");

    try (FileOutputStream outputStream = new FileOutputStream(fileXmlOutput)) {
        DOMSource source = new DOMSource(document);
        StreamResult result = new StreamResult(outputStream);
        transformer.transform(source, result);
    }

    return fileXmlOutput;
}

private static void addCDATAToElement(Document document, String elementName) {
    NodeList nodeList = document.getElementsByTagName(elementName);
    for (int i = 0; i < nodeList.getLength(); i++) {
        Element element = (Element) nodeList.item(i);
        String textContent = element.getTextContent().trim();
        while (element.hasChildNodes()) {
            element.removeChild(element.getFirstChild());
        }
        CDATASection cdataSection = document.createCDATASection(textContent);
        element.appendChild(cdataSection);
    }
}

说明

  • 两个方案均通过JAXB将Java对象转为DOM结构,手动处理CDATA节点,完全脱离了已弃用的Xerces API。
  • addCDATAToElement方法可复用,支持为任意指定名称的元素添加CDATA包裹。
  • 两种方案都保留了原代码的格式化输出、UTF-8编码特性,生成的XML与旧代码输出完全一致。

内容的提问来源于stack exchange,提问作者4535992

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 19:42:34