简体   繁体   中英

JAXB Won't Accept CustomEscapeHandler

I'm working on an application that does a lot of XML manipulation, so I'm trying to get JAXB working in it. I have the following class for a CustomEscapeHandler: import com.sun.xml.bind.marshaller.CharacterEscapeHandler;

import java.io.IOException;
import java.io.Writer;

public class CustomEscapeHandler implements CharacterEscapeHandler {
    private static final CharacterEscapeHandler theInstance = new CustomEscapeHandler();

    private CustomEscapeHandler() {
    }

    public static CharacterEscapeHandler getInstance() {
        return theInstance;
    }

    public void escape(char[] ch, int start, int length, boolean isAttVal, Writer out) throws IOException {
        int limit = start + length;
        for(int i = start; i < limit; ++i) {
            char c = ch[i];
            if (c == 38 || c == 39 || c == 60 || c == 62 || c == 13 || c == 34 && isAttVal) {
                if (i != start) {
                    out.write(ch, start, i - start);
                }

                start = i + 1;
                switch(ch[i]) {
                    case '\"':
                        out.write("&quot;");
                        break;
                    case '&':
                        out.write("&amp;");
                        break;
                    case '<':
                        out.write("&lt;");
                        break;
                    case '>':
                        out.write("&gt;");
                        break;
                    case '\'':
                        out.write("&apos;");
                        break;
                }
            }
        }

        if (start != limit) {
            out.write(ch, start, limit - start);
        }
    }
}

If I use

jaxbMarshaller.setProperty("com.sun.xml.internal.bind.characterEscapeHandler", CustomEscapeHandler.getInstance());

I get

javax.xml.bind.PropertyException: property "com.sun.xml.internal.bind.characterEscapeHandler" must be an instance of type com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler, not my.package.CustomEscapeHandler

So if I change it to

jaxbMarshaller.setProperty("com.sun.xml.bind.marshaller.CharacterEscapeHandler", CustomEscapeHandler.getInstance());

I get

javax.xml.bind.PropertyException: name: com.sun.xml.bind.marshaller.CharacterEscapeHandler value: my.package.CustomEscapeHandler@e2b4f3
    at javax.xml.bind.helpers.AbstractMarshallerImpl.setProperty(AbstractMarshallerImpl.java:358)
    at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.setProperty(MarshallerImpl.java:527)
    at my.package.util.XmlUtils.convert(XmlUtils.java:39)
    at my.package.util.XmlUtils.convertFormToXml(XmlUtils.java:28)
    at my.package.FormsMain.saveXML(FormsMain.java:362)
    at my.package.FormsMain.saveForms(FormsMain.java:499)
    at my.package.FormsMain.lambda$createWindow$3(FormsMain.java:231)

Looking at the stacktrace, it's trying to use the marshaller in the internal packages, which also looking at the decompiled code, is expecting the the escape handler to be an implementation of the CharacterEscapeHandler in the internal package.

How can I get JAXB to not use the internal package implementation?

A month later, still have been unable to find a solution. Every example I see online has extending the internal version of CharacterEscapeHandler.

The problem here is using CharacterEscapeHandler from internal "com.sun.xml.internal" package. It is not good idea, even if your code will work. Instead use similar class from com.sun.xml.bind.marshaller.CharacterEscapeHandler and it will resolve the issue for you.

You should be able to control what marshaller is used by setting a startup property:

 javax.xml.bind.context.factory=com.sun.xml.bind.v2.ContextFactory

That sets it to the JAXB reference implementation rather than the internal context factory, and correspondingly the marshaller you get will be of type com.sun.xml.bind.v2.runtime.MarshallerImpl rather than the internal one.

If you look inside the JAXBContext class, in the newInstance method, you will see that it invokes a class ContextFinder.find, and it is that method which reads the javax.xml.bind.context.factory property to know what JAXB context factory to use.

The following startup property did solve it for me (none of the other solutions did work).

Add the following start option to your java program / application server / spring boot app ...:

-Djavax.xml.bind.JAXBContextFactory=com.sun.xml.bind.v2.ContextFactory

It might have to do with the addtional entry in the jaxb.jar / META-INF/services folder which wasn't there before...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM