JAXB unmarshalling of “generic” real world documents

馋奶兔 提交于 2019-12-02 04:54:12

问题


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

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