问题
We have a large set of configuration documents of the style
<foo>
<bar class="com.diedel.Gnarf">
...more content
</bar>
<bar class="de.doedel.Bork">
..more content
</bar>
...
</foo>
The generic implementation classes are all mapped using Jaxb themselves.
Before i finally revert to Xstream, a question to the community: Is there a (non hack) way to get this unmarshalled in Jaxb. What we tried so far is "nested" unmarshalling using an @XMLAdapter, where the "bar" field gets an Element, gets the target class and calls another "unmarshall" cycle. This approach has been a) quite slow and b) is broken with the switch to Java 7 where some magic ThreadLocal destroys the context when returning from the inner unmarshall.
I found that MOXy may support this using @XmlDiscriminatorNode - this looks like a good solution and i really like the very good input of Blaise - but adding a >7MB lib just for fun is not an option.
Other ideas?
回答1:
You could do something like the following with JAXB (JSR-222) and StAX (JSR-173):
Demo
A StAX XMLStreamReader
can be used to parse the XML document. You can use it to advance to a bar
element, then you can read the class
attribute and load the appropriate Java class from a ClassLoader
. Then you can leverage one of the unmarshal
methods that takes a class parameter.
package forum12402215;
import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
ClassLoader classLoader = Bork.class.getClassLoader();
JAXBContext jc = JAXBContext.newInstance(Bork.class, Gnarf.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StreamSource source = new StreamSource("src/forum12402215/input.xml");
XMLInputFactory xif = XMLInputFactory.newFactory();
XMLStreamReader xsr = xif.createXMLStreamReader(source);
xsr.nextTag(); // Advance to "foo" element
xsr.nextTag(); // Advance to "bar" element
while(xsr.getLocalName().equals("bar")) {
String className = xsr.getAttributeValue("", "class");
Class<?> clazz = classLoader.loadClass(className);
Object object = unmarshaller.unmarshal(xsr, clazz).getValue();
System.out.println(object);
xsr.nextTag();
}
}
}
Bork
Below is a sample Bork
class.
package forum12402215;
public class Bork {
private String b;
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
@Override
public String toString() {
return "Bork(b=" + b + ")";
}
}
Gnarf
Below is a sample Gnarf
class:
package forum12402215;
public class Gnarf {
private int a;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
@Override
public String toString() {
return "Gnarf(a=" + a + ")";
}
}
input.xml
Below is he sample document I used used for this example based on the one from your question. I changed the package names.
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<bar class="forum12402215.Gnarf">
<a>123</a>
</bar>
<bar class="forum12402215.Bork">
<b>Hello World</b>
</bar>
</foo>
Output
Below is the output from running the demo code.
Gnarf(a=123)
Bork(b=Hello World)
回答2:
If the structure really is that simple I'd be tempted to parse the top two levels (foo and the class attribute of bar) myself using an XMLStreamReader
, then hand the reader off to the JAXB unmarshaller to parse each bar, assembling the results into a list. I wouldn't bother trying to parse the outer envelope layers using JAXB directly.
来源:https://stackoverflow.com/questions/12402215/jaxb-unmarshalling-of-generic-real-world-documents