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

解析含BMP外字符的XML时输出实体异常,求排查原因

问题解析:XML中十六进制实体转义异常

我尝试解析包含十六进制实体𝓅(对应数学符号𝓅)的XML文件,输出却变成了��,请问哪里操作有误?

示例输入XML

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <data>&#x1d4c5;</data>
</root>

示例输出XML

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <data>&#55349;&#56517;</data>
</root>

相关代码

获取XML Reader的代码

factory = org.apache.xerces.jaxp.SAXParserFactoryImpl.newInstance();
final XMLReader xmlReader;
xmlReader = factory.newSAXParser().getXMLReader();

读写XML的方法代码

public void readAndWriteXml(InputSource inputSource, OutputStream out) throws IOException, SAXException, ParserConfigurationException {

    XMLReader xmlReader = getXmlReader();
    Serializer serializer = SerializerFactory.getSerializer(configProps);
    serializer.setOutputStream(out);
    xmlReader.setContentHandler(serializer.asContentHandler());

    if(logger != null){
        getLogger().debug("starting xml parsing" + LocalTime.now());
    }
    xmlReader.parse(inputSource);
    if(logger != null){
        getLogger().debug("end xml parsing" + LocalTime.now());
    }

}

getXMLReader()方法代码

final XMLReader xmlReader;
xmlReader = factory.newSAXParser().getXMLReader();
xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
xmlReader.setFeature("http://xml.org/sax/features" +
        "/namespaces", true);
xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", true);
//        xmlReader.setFeature("http://xml.org/sax/features/validation", true);
xmlReader.setEntityResolver(wrappedEntityResolver);
xmlReader.setErrorHandler(new SaxErrorHandler());
return xmlReader;

类初始化代码

public XmlNormalizer(String catalogPath) throws IOException {
    // We want the Apache XML parser, not the embedded Oracle Java version.
    factory = org.apache.xerces.jaxp.SAXParserFactoryImpl.newInstance();
    factory.setNamespaceAware(true);
    List<Path> catalogFiles = this.findByFileName(new File(catalogPath).toPath(), CATALOG_FILENAME_PATTERN);
    String[] catalogArray = catalogFiles.stream().map(Path::toString).toArray(String[]::new);
    configProps = OutputPropertiesFactory.getDefaultMethodProperties("xml");
    XMLCatalogResolver xmlCatalogResolver = new XMLCatalogResolver(catalogArray, true);
    wrappedEntityResolver = new WrappedEntityResolver(xmlCatalogResolver);
}

WrappedEntityResolver是org.apache.xerces.util.XMLCatalogResolver的简单包装类。

问题原因与解决办法

原因解析

&#x1d4c5;对应的Unicode字符是U+1D4C5,属于增补平面字符(超出基本多语言平面BMP的字符)。Java内部用UTF-16编码表示字符,增补平面字符会被拆分为两个UTF-16代理项:U+D835(十进制55349)和U+DCC5(十进制56517)。你看到的输出就是这两个代理项被序列化器转义后的十进制实体形式,这不是解析错误,是默认序列化规则导致的结果。

解决办法

  1. 让序列化器直接输出UTF-8字符
    修改类初始化中configProps的配置,添加属性禁止转义非ASCII字符,让序列化器直接输出原始UTF-8字符:

    configProps = OutputPropertiesFactory.getDefaultMethodProperties("xml");
    // 配置输出编码与转义规则
    configProps.setProperty("encoding", "UTF-8");
    configProps.setProperty("http://xml.apache.org/xerces/properties/serializer/escape-non-ascii", "false");
    
  2. 保留原始十六进制实体(可选)
    如果必须输出原始的十六进制实体而非字符,需要自定义解析逻辑——在解析时记录原始实体字符串,而非解析后的字符。因为标准XML解析器会自动将实体解析为对应字符,后续序列化只能基于字符处理,无法直接还原原始实体格式。

  3. 升级Xerces版本
    确保使用的Apache Xerces解析器和序列化器是最新稳定版,旧版本对增补平面字符的处理可能存在兼容性问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 14:54:59