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

如何让XStream生成缩进一致的CDATA内容XML输出?

解决XStream生成XML时CDATA内容缩进问题

使用XStream结合自定义CdataXppDriver生成XML时,CDATA块内的多行内容未按XML层级缩进,不符合格式要求。

当前输出

<java.util.Arrays_-ArrayList>
  <a class="sct.commons.xml.TestObject-array">
    <sct.commons.xml.TestObject>
      <normalText>123</normalText>
      <cdataText><![CDATA[456]]></cdataText>
    </sct.commons.xml.TestObject>
    <sct.commons.xml.TestObject>
      <normalText>abc</normalText>
      <cdataText><![CDATA[def]]></cdataText>
    </sct.commons.xml.TestObject>
    <sct.commons.xml.TestObject>
      <normalText>some text</normalText>
      <cdataText><![CDATA[Lorem ipsum dolor sit amet
consectetur adipisici elit
sed eiusmod tempor incidunt ut labore et dolore magna aliqua
]]></cdataText>
    </sct.commons.xml.TestObject>
  </a>
</java.util.Arrays_-ArrayList>

期望效果

最小方案(CDATA内多行缩进对齐)

<java.util.Arrays_-ArrayList>
  <a class="sct.commons.xml.TestObject-array">
    <sct.commons.xml.TestObject>
      <normalText>123</normalText>
      <cdataText><![CDATA[456]]></cdataText>
    </sct.commons.xml.TestObject>
    <sct.commons.xml.TestObject>
      <normalText>abc</normalText>
      <cdataText><![CDATA[def]]></cdataText>
    </sct.commons.xml.TestObject>
    <sct.commons.xml.TestObject>
      <normalText>some text</normalText>
      <cdataText><![CDATA[Lorem ipsum dolor sit amet
        consectetur adipisici elit
        sed eiusmod tempor incidunt ut labore et dolore magna aliqua
        ]]></cdataText>
    </sct.commons.xml.TestObject>
  </a>
</java.util.Arrays_-ArrayList>

最优方案(CDATA块独立换行+内部缩进)

<java.util.Arrays_-ArrayList>
  <a class="sct.commons.xml.TestObject-array">
    <sct.commons.xml.TestObject>
      <normalText>123</normalText>
      <cdataText><![CDATA[456]]></cdataText>
    </sct.commons.xml.TestObject>
    <sct.commons.xml.TestObject>
      <normalText>abc</normalText>
      <cdataText><![CDATA[def]]></cdataText>
    </sct.commons.xml.TestObject>
    <sct.commons.xml.TestObject>
      <normalText>some text</normalText>
      <cdataText>
        <![CDATA[
        Lorem ipsum dolor sit amet
        consectetur adipisici elit
        sed eiusmod tempor incidunt ut labore et dolore magna aliqua
        ]]>
      </cdataText>
    </sct.commons.xml.TestObject>
  </a>
</java.util.Arrays_-ArrayList>

当前核心代码

XML生成代码

XStream xStream = new XStream(cdataXppDriver);
String xml = xStream.toXML(model);

自定义CdataXppDriver代码

public class CdataXppDriver extends XppDriver
{
    private final List<String> CDATA_FIELDS;

    public CdataXppDriver(List<String> cdataFields)
    {
        super();
        CDATA_FIELDS = new ArrayList<>();
        if (cdataFields != null)
        {
            CDATA_FIELDS.addAll(cdataFields);
        }
    }

    @Override
    public HierarchicalStreamWriter createWriter(Writer out)
    {
        return new PrettyPrintWriter(out, getNameCoder())
        {
            boolean cdata = false;

            @Override
            public void startNode(String name)
            {
                super.startNode(name);
                cdata = CDATA_FIELDS.contains(name);
            }

            @Override
            protected void writeText(QuickWriter writer, String text)
            {
                if (cdata)
                {
                    writer.write("<![CDATA[");
                    writer.write(text);
                    writer.write("]]>");
                }
                else
                {
                    super.writeText(writer, text);
                }
            }
        };
    }
}

解决方案

最小方案实现

修改PrettyPrintWriter的writeText方法,对CDATA内的多行文本添加当前层级的缩进:

@Override
public HierarchicalStreamWriter createWriter(Writer out)
{
    return new PrettyPrintWriter(out, getNameCoder())
    {
        boolean cdata = false;
        // 获取当前CDATA内容的缩进字符串
        private String getCurrentIndent() {
            StringBuilder indent = new StringBuilder();
            for (int i = 0; i < getDepth(); i++) {
                indent.append(getIndent());
            }
            // 额外增加一层缩进匹配cdataText节点的层级
            indent.append(getIndent());
            return indent.toString();
        }

        @Override
        public void startNode(String name)
        {
            super.startNode(name);
            cdata = CDATA_FIELDS.contains(name);
        }

        @Override
        protected void writeText(QuickWriter writer, String text)
        {
            if (cdata)
            {
                writer.write("<![CDATA[");
                // 拆分多行文本并添加缩进
                String[] lines = text.split(System.lineSeparator());
                for (int i = 0; i < lines.length; i++) {
                    if (i > 0) {
                        writer.write(System.lineSeparator());
                        writer.write(getCurrentIndent());
                    }
                    writer.write(lines[i]);
                }
                writer.write("]]>");
            }
            else
            {
                super.writeText(writer, text);
            }
        }
    };
}

最优方案实现

不仅处理CDATA内部缩进,还将CDATA块独立换行,与父节点层级对齐:

@Override
public HierarchicalStreamWriter createWriter(Writer out)
{
    return new PrettyPrintWriter(out, getNameCoder())
    {
        boolean cdata = false;
        // 获取当前节点的缩进字符串
        private String getCurrentNodeIndent() {
            StringBuilder indent = new StringBuilder();
            for (int i = 0; i < getDepth(); i++) {
                indent.append(getIndent());
            }
            return indent.toString();
        }
        // 获取CDATA内容的缩进字符串(比当前节点多一层)
        private String getCdataContentIndent() {
            return getCurrentNodeIndent() + getIndent();
        }

        @Override
        public void startNode(String name)
        {
            super.startNode(name);
            cdata = CDATA_FIELDS.contains(name);
        }

        @Override
        protected void writeText(QuickWriter writer, String text)
        {
            if (cdata)
            {
                // 换行并添加当前节点缩进,写入CDATA开头
                writer.write(System.lineSeparator());
                writer.write(getCdataContentIndent());
                writer.write("<![CDATA[");
                // 拆分多行文本并添加内容缩进
                String[] lines = text.split(System.lineSeparator());
                for (int i = 0; i < lines.length; i++) {
                    writer.write(System.lineSeparator());
                    writer.write(getCdataContentIndent());
                    writer.write(lines[i]);
                }
                // 换行并添加内容缩进,写入CDATA结尾
                writer.write(System.lineSeparator());
                writer.write(getCdataContentIndent());
                writer.write("]]>");
                // 换行并添加当前节点缩进,闭合标签前的换行
                writer.write(System.lineSeparator());
                writer.write(getCurrentNodeIndent());
            }
            else
            {
                super.writeText(writer, text);
            }
        }
    };
}

说明

  • getDepth():获取当前XML节点的层级深度,用于计算缩进次数
  • getIndent():获取PrettyPrintWriter默认的缩进字符串(默认是两个空格,可通过构造函数自定义)
  • 两种方案都兼容单行CDATA内容的格式化,不会破坏原有单行输出的格式

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 03:10:57