如何解析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", " "); } 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()); // 输出  } }
三、更直接的底层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); // 直接输出  } }
核心思路总结
所有方案的核心都是绕开解析器的自动实体解码:要么在解析后把解码后的字符替换回原始实体字符串,要么直接配置解析器禁止扩展实体(DOM方案最直接)。
内容的提问来源于stack exchange,提问作者Igor Piddubnyi
相关产品推荐
相关产品推荐

