【问题标题】:JAXB unmarshalling of "generic" real world documentsJAXB 对“通用”现实世界文档进行解组
【发布时间】:2012-09-13 08:25:03
【问题描述】:

我们有大量的样式配置文件

<foo>
  <bar class="com.diedel.Gnarf">
    ...more content
  </bar>
  <bar class="de.doedel.Bork">
    ..more content
  </bar>
  ...
</foo>

通用实现类都使用 Jaxb 本身进行映射。

在我最终恢复到 Xstream 之前,向社区提出一个问题:有没有(非 hack)方法可以在 Jaxb 中解组这个问题。到目前为止,我们尝试的是使用@XMLAdapter 进行“嵌套”解组,其中“bar”字段获取一个元素,获取目标类并调用另一个“解组”循环。这种方法 a) 非常慢,并且 b) 在切换到 Java 7 时被破坏,其中一些神奇的 ThreadLocal 在从内部解组返回时破坏了上下文。

我发现 MOXy 可以使用 @XmlDiscriminatorNode 支持这一点 - 这看起来是一个很好的解决方案,我真的很喜欢 Blaise 的非常好的输入 - 但添加一个 >7MB 的库只是为了好玩不是一种选择。

其他想法?

【问题讨论】:

  • MOXy 的 @XmlDescriminatorNode 扩展对于映射继承关系 (blog.bdoughan.com/2010/11/…) 很有用。如果使用捆绑包而不是包含 JPA 和 SDO 实现的整个 eclipselink.jar,则 MOXy 的安装占用空间更小。我个人正在进一步减少占用空间,您可以使用以下错误跟踪我的进度:bugs.eclipse.org/384399

标签: java jaxb unmarshalling


【解决方案1】:

您可以使用JAXB (JSR-222)StAX (JSR-173) 执行以下操作:

演示

StAX XMLStreamReader 可用于解析 XML 文档。您可以使用它前进到bar 元素,然后可以读取class 属性并从ClassLoader 加载适当的Java 类。然后,您可以利用采用类参数的unmarshal 方法之一。

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 类。

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 类的示例:

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

以下是我用于此示例的示例文档,基于您的问题。我更改了包名。

<?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>

输出

下面是运行演示代码的输出。

Gnarf(a=123)
Bork(b=Hello World)

【讨论】:

  • 再次感谢您快速而详细的回复。也就是说,我必须补充一点,确实需要一种通用的方式。一方面, 更复杂,并且属性也在其上编组。其次,“通用”实例存在于许多地方和任何嵌套深度。但是您的回答指向另一个选择:我应该能够使用将 class="x.y.Z" 映射到 xsi:type="Z" 的 sax 过滤器来做到这一点?
  • @mtraut - 是的,您可以使用 SAX 过滤器。这是我的一个答案的链接,可能有助于采用这种方法:stackoverflow.com/a/11970622/383861
  • 嗯,我会试一试(虽然它仍然具有 hack 的味道)。有没有一种简单的方法来保持完全限定的类名有效?
  • 我会给这个答案作为奖励,即使我承认来自@Ian 的几乎相同的建议。但是你总是投入大量的工作。谢谢你。
【解决方案2】:

如果结构真的这么简单,我会很想自己使用XMLStreamReader 解析前两个级别(foo 和 bar 的类属性),然后将阅读器交给 JAXB 解组器来解析每个 bar ,将结果组装成一个列表。我不会费心尝试直接使用 JAXB 解析外部信封层。

【讨论】:

  • +1 - 你在我之前得到了答案。如果您好奇这种方法的 impl 是什么样子,请查看:stackoverflow.com/a/12402813/383861
  • 感谢您的快速回复。由于您的答案与@Blaise one 非常相似,因此我只会评论一次....
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-02-10
  • 1970-01-01
  • 1970-01-01
  • 2011-04-04
  • 2014-02-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多