问题
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