让 JAXB 转义撇号

Make JAXB escape apostrophe

我正在使用 JAXB 将 Java 对象转换为 XML。我需要在输出 XML 中将撇号 (') 字符转义为 '。默认情况下,JAXB 不会转义撇号字符。我通过实现 CharacterEscapeHandler 了解了如何实现自定义转义处理程序。但是,我意识到 class 不包含在 Java 1.7 中。我在哪里可以获得包含 class 的最新库?此下载的 JAXB 库中的 classes 是否会与 Java 1.7 中的 JAXB classes 冲突?

为了不依赖于 JRE 的其他 versions/implementations 中不可用的内部 类,您应该编写自己的 ContentHandler (SAX) or XMLStreamWriter (StAX)。这样你就可以完全控制输出。

这是一个例子。它显示了 JAXB 通常如何输出 XML 以及如何使用 ContentHandler 自行输出。这很简单,例如性能不是很好,也没有命名空间支持。您可能想要编写更好版本的 escape() 方法。您还可能希望将输出写入文件,而不是控制台。

public class Main {
    public static void main(String[] args) throws Exception {
        JAXBContext jaxbContext = JAXBContext.newInstance(MyData.class);
        Marshaller marshaller = jaxbContext.createMarshaller();
        marshaller.marshal(new MyData("Test < ' \" & >", "Test < ' \" & >"), System.out);

        System.out.println();

        marshaller.marshal(new MyData("Test < ' \" & >", "Test < ' \" & >"), new MyContentHandler());
    }
}
@XmlRootElement
class MyData {
    @XmlAttribute private String name;
    @XmlElement   private String desc;
    public MyData() {}
    public MyData(String name, String desc) { this.name = name; this.desc = desc; }
}
class MyContentHandler implements ContentHandler {
    @Override
    public void setDocumentLocator(Locator locator) {
        // Nothing to do
    }
    @Override
    public void startDocument() throws SAXException {
        System.out.print("<?xml version=\"1.0\" encoding=\"" + Charset.defaultCharset() + "\" standalone=\"yes\"?>");
    }
    @Override
    public void endDocument() throws SAXException {
        // Nothing to do
    }
    @Override
    public void startPrefixMapping(String prefix, String uri) throws SAXException {
        throw new UnsupportedOperationException();
    }
    @Override
    public void endPrefixMapping(String prefix) throws SAXException {
        throw new UnsupportedOperationException();
    }
    @Override
    public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
        System.out.print('<' + localName);
        for (int i = 0; i < atts.getLength(); i++)
            System.out.print(' ' + atts.getLocalName(i) + "=\"" + escape(atts.getValue(i)) + '"');
        System.out.print('>');
    }
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        System.out.print("</" + localName + ">");
    }
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        System.out.print(escape(new String(ch, start, length)));
    }
    private static String escape(String text) {
        return text.replace("&", "&amp;")
                   .replace("<", "&lt;")
                   .replace(">", "&gt;")
                   .replace("'", "&apos;")
                   .replace("\"", "&quot;");
    }
    @Override
    public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
        throw new UnsupportedOperationException();
    }
    @Override
    public void processingInstruction(String target, String data) throws SAXException {
        throw new UnsupportedOperationException();
    }
    @Override
    public void skippedEntity(String name) throws SAXException {
        throw new UnsupportedOperationException();
    }
}

输出

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><myData name="Test &lt; ' &quot; &amp; &gt;"><desc>Test &lt; ' " &amp; &gt;</desc></myData>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><myData name="Test &lt; &apos; &quot; &amp; &gt;"><desc>Test &lt; &apos; &quot; &amp; &gt;</desc></myData>

虽然我喜欢 Andreas 的回答,但我不喜欢从头开始编写自己的 XmlStreamWriter。我建议使用 Woodstox 的实现并提供您自己的 EscapingWriterFactory。类似于:

XMLOutputFactory2 xmlOutputFactory = (XMLOutputFactory2)XMLOutputFactory.newFactory();
xmlOutputFactory.setProperty(XMLOutputFactory2.P_TEXT_ESCAPER, new EscapingWriterFactory() {

    @Override
    public Writer createEscapingWriterFor(Writer w, String enc) {
        return new EscapingWriter(w);
    }

    @Override
    public Writer createEscapingWriterFor(OutputStream out, String enc) throws UnsupportedEncodingException {
        return new EscapingWriter(new OutputStreamWriter(out, enc));
    }

});

marshaller.marshal(model, xmlOutputFactory.createXMLStreamWriter(out);

如何编写 EscapingWriter 的示例可以在 CharacterEscapingTest 中看到。