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

如何解析XML属性descr的原始未转换值?(JAXB/Jackson)

如何保留XML属性中的原始实体引用(如 )?

我来帮你解决这个问题——你遇到的是XML解析器默认自动解码实体引用的情况,不管JAXB还是Jackson,底层解析器都会把 转成对应的非换行空格,所以直接拿不到原始的实体字符串。下面分框架给你具体的解决办法,还有更通用的底层解析方案:

一、JAXB解决方案

JAXB默认依赖JDK自带的SAX解析器,要保留原始实体,我们可以通过自定义XMLReader拦截属性解析过程,把解码后的字符替换回原始实体字符串:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "declaration")
public class DeclarationTestJaxb {
    @XmlAttribute
    private String descr;

    public String getDescr() {
        return descr;
    }

    public void setDescr(String descr) {
        this.descr = descr;
    }

    @Override
    public String toString() {
        return descr;
    }

    public static void main(String[] args) throws JAXBException, SAXException {
        JAXBContext jaxbContext = JAXBContext.newInstance(DeclarationTestJaxb.class);
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();

        // 自定义XMLReader,拦截实体引用处理
        XMLReader xmlReader = XMLReaderFactory.createXMLReader();
        xmlReader.setContentHandler(new DefaultHandler() {
            private UnmarshallerHandler unmarshallerHandler;
            private boolean inDeclaration = false;

            public void setUnmarshallerHandler(UnmarshallerHandler handler) {
                this.unmarshallerHandler = handler;
            }

            @Override
            public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
                if ("declaration".equals(localName)) {
                    inDeclaration = true;
                    // 手动处理descr属性,把解码后的空格替换回原始实体
                    String rawDescr = attributes.getValue("descr");
                    String processedDescr = rawDescr.replace("\u00A0", " ");
                    // 替换属性值后传给JAXB的解析处理器
                    AttributesImpl newAttrs = new AttributesImpl(attributes);
                    int index = newAttrs.getIndex("descr");
                    if (index != -1) {
                        newAttrs.setValue(index, processedDescr);
                    }
                    unmarshallerHandler.startElement(uri, localName, qName, newAttrs);
                } else {
                    unmarshallerHandler.startElement(uri, localName, qName, attributes);
                }
            }

            @Override
            public void endElement(String uri, String localName, String qName) throws SAXException {
                if ("declaration".equals(localName)) {
                    inDeclaration = false;
                }
                unmarshallerHandler.endElement(uri, localName, qName);
            }

            @Override
            public void characters(char[] ch, int start, int length) throws SAXException {
                unmarshallerHandler.characters(ch, start, length);
            }
        });

        UnmarshallerHandler handler = unmarshaller.getUnmarshallerHandler();
        ((DefaultHandler) xmlReader.getContentHandler()).setUnmarshallerHandler(handler);

        // 执行解析
        xmlReader.parse(new InputSource(new FileReader("test.xml")));
        DeclarationTestJaxb result = (DeclarationTestJaxb) handler.getResult();
        System.out.println(result); // 现在输出的是 
    }
}

二、Jackson解决方案

Jackson XML模块同样会自动解码实体,我们可以通过自定义反序列化器,把解码后的字符还原为原始实体字符串:

第一步:定义自定义反序列化器

public class RawEntityAttributeDeserializer extends StdDeserializer<String> {
    protected RawEntityAttributeDeserializer() {
        super(String.class);
    }

    @Override
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        if (p instanceof XmlParser) {
            XmlParser xmlParser = (XmlParser) p;
            XMLStreamReader reader = xmlParser.getStaxReader();
            // 获取属性值后替换回原始实体
            String attrValue = reader.getAttributeValue(null, "descr");
            return attrValue.replace("\u00A0", "&#xA0;");
        }
        return p.getValueAsString();
    }
}

第二步:在实体类中使用该反序列化器

@Data
@JacksonXmlRootElement(localName = "declaration")
public class DeclarationTestJackson {
    @JacksonXmlProperty(isAttribute = true)
    @JsonDeserialize(using = RawEntityAttributeDeserializer.class)
    private String descr;

    public static void main(String[] args) throws IOException {
        XmlMapper xmlMapper = new XmlMapper();
        DeclarationTestJackson result = xmlMapper.readValue(new File("test.xml"), DeclarationTestJackson.class);
        System.out.println(result.getDescr()); // 输出&#xA0;
    }
}

三、更直接的底层XML解析方案(DOM)

如果不想依赖JAXB或Jackson,直接用DOM解析可以更简单地保留原始实体,只需要配置解析器禁止自动扩展实体即可:

public class RawXmlParser {
    public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        // 关键配置:禁止自动扩展实体引用
        factory.setExpandEntityReferences(false);
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(new File("test.xml"));
        Element declaration = (Element) doc.getElementsByTagName("declaration").item(0);
        String rawDescr = declaration.getAttribute("descr");
        System.out.println(rawDescr); // 直接输出&#xA0;
    }
}

核心思路总结

所有方案的核心都是绕开解析器的自动实体解码:要么在解析后把解码后的字符替换回原始实体字符串,要么直接配置解析器禁止扩展实体(DOM方案最直接)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 14:13:10