How to marshall java.lang.Exception using JAXB Marshaller?

回眸只為那壹抹淺笑 提交于 2019-12-24 20:38:45

问题


I am trying to marshall java.lang.Exception, but without success. Here's my code -

import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

public class JAXBTester
{
    public static void main(String[] args) throws Exception
    {
       TestReport report = new TestReport();
       report.setReportLog("Tests successful.");

       File file = new File("TestReport.xml");
       JAXBContext jaxbContext = JAXBContext.newInstance(TestReport.class);
       Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

       jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
       jaxbMarshaller.marshal(report, file);
    }
}

This is the class I want to marshall -

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class TestReport
{
    private String reportLog;
    private Exception exception;

    @XmlElement
    public void setReportLog(String reportLog) { this.reportLog = reportLog; }

    public String getReportLog() { return reportLog; }

    @XmlElement
    public void setException(Exception exception) { this.exception = exception; }

    public Exception getException() { return exception; }

}

I get the following exception -

Exception in thread "main" com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
java.lang.StackTraceElement does not have a no-arg default constructor.
    this problem is related to the following location:
        at java.lang.StackTraceElement
        at public java.lang.StackTraceElement[] java.lang.Throwable.getStackTrace()
        at java.lang.Throwable
        at java.lang.Exception
        at public java.lang.Exception TestReport.getException()
        at TestReport

    at com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:91)
    at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:451)
    at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:283)
    at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:126)
    at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1142)
    at com.sun.xml.internal.bind.v2.ContextFactory.createContext(ContextFactory.java:130)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:248)
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:235)
    at javax.xml.bind.ContextFinder.find(ContextFinder.java:445)
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:637)
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:584)
    at JAXBTester.main(JAXBTester.java:14)

This is because I am trying to marshall java.lang.Exception. How to solve this problem?


回答1:


There are other ways of doing it, such as the one referenced by the commenter. But here's another way...

The Throwable class in Java (superclass of Exception) is serializable in the sense of java.io.Serializable. This means that you can write it to a byte stream and then recompose it later from those bytes. (It's possible that your application might have poorly-written non-serializable subclasses of Throwable. If that's the case, the following won't work.)

So one way to deal with this is to write a custom adapter that serializes the Throwable (or Exception) to bytes. And in the XML, you see the hex for those bytes. Then on the receiving end you can un-serialize and then work with (an exact replica of) the Throwable you started with.

The bad part about this way is that the Exception is not human-readable inside the XML. The good part is that it's really simple. On your TestReport class, put this annotation on your Exception getter:

@XmlJavaTypeAdapter(ThrowableAdapter.class)
public Exception getException() { return exception; }

public void setException(Exception exception) { this.exception = exception; }

And then add this adapter class to your project:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
import javax.xml.bind.annotation.adapters.XmlAdapter;

public class ThrowableAdapter extends XmlAdapter<String, Throwable> {
    private HexBinaryAdapter hexAdapter = new HexBinaryAdapter();

    @Override
    public String marshal(Throwable v) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(v);
        oos.close();
        byte[] serializedBytes = baos.toByteArray();
        return hexAdapter.marshal(serializedBytes);
    }

    @Override
    public Throwable unmarshal(String v) throws Exception {
        byte[] serializedBytes = hexAdapter.unmarshal(v);
        ByteArrayInputStream bais = new ByteArrayInputStream(serializedBytes);
        ObjectInputStream ois = new ObjectInputStream(bais);
        Throwable result = (Throwable) ois.readObject();
        return result;
    }
}

Then your XML will contain an element like this:

<exception>AED...</exception>

except in instead of ... you'll see a huge hex string. When it's un-marshalled on the other side, it'll be just like the original.




回答2:


Do you use JRE 1.7? I do not get this exception if I use JAXB version that is distributed with the JRE. However, if I include explicitly JAXB v. 2.1.7, I run into this problem. Therefore, I'd recommend getting rid of all the jaxb instances from your classpath and use the one that is included in the Java runtime.

JRE 6 seems to use 2.1.x, while JRE 7 2.2.x, which probably handles the change in Throwable implementation correctly.

The JAXB version included in your JDK can be found by running

<JDK_HOME>/bin/xjc -version


来源:https://stackoverflow.com/questions/11369582/how-to-marshall-java-lang-exception-using-jaxb-marshaller

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!