【问题标题】:JAXB @XmlAdapter for arbitrary XML用于任意 XML 的 JAXB @XmlAdapter
【发布时间】:2013-08-16 11:14:58
【问题描述】:

我有一个org.w3c.dom.Element,我从我的XmlAdapter 返回一个自定义@XmlElement,我想将它作为任意XML 的JAXB 对象的一部分包含在内(我知道我会必须手工制作 XSD)。但是,JAXB 抱怨

org.w3c.dom.Element is an interface, and JAXB can't handle interfaces.

显然 w3c XML 类型是not supported as Java types,这是一种耻辱。但除此之外,当我使用显然受支持的javax.xml.transform.Result 时,我也会遇到同样的错误。

如何在 JAXB 中包含任意 XML 元素作为元素?

注意:按照https://forums.oracle.com/thread/1668210我也试过了

MessageFactory factory = MessageFactory.newInstance();
message = factory.createMessage();          
SOAPElement element = message.getSOAPBody().addDocument(doc);

但这也给出了同样的错误。

【问题讨论】:

  • 你看过@XmlAnyElement吗?
  • @MattR 我认为 XSD 映射更重要的是:您仍然需要将支持的对象类型交给 JAXB,以便将其转换为 XML。
  • 我已经用它来解组我没有专门建模的 XML 元素,然后将其编组回 XML,所以我猜你也可以创建自己的 List<Element> 注释 @XmlAnyElement它会编组它......
  • @MattR 这可能是因为您的 bean 符合上面链接的文档中描述的规则(公共构造函数、getter/setter、JAXB 支持的所有原始类型)。
  • 所以你的 bean 没有任何部分是有效的?也许我误解了你的要求,你能显示一些代码吗?

标签: java xml jakarta-ee dom jaxb


【解决方案1】:

TL;DR

只要您将值类型指定为Object(而不是Element),您就可以拥有一个XmlAdapter,它将您的域对象转换为org.w3c.dom.Element 的实例。


下面是一个完整的例子。

XmlAdapter

java.lang.Object 类型的字段/属性会将未知内容保留为 DOM 节点。您可以通过将XmlAdapter 中的值类型指定为Object 来在您的用例中利用这一点。您需要确保从 marshal 方法返回的根元素与 @XmlElement 注释定义的字段/属性相匹配。

import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.parsers.*;
import org.w3c.dom.*;

public class BarAdapter extends XmlAdapter<Object, Bar>{

    private DocumentBuilder documentBuilder;

    public BarAdapter() {
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            documentBuilder = dbf.newDocumentBuilder();
        } catch(Exception e) {
            // TODO - Handle Exception
        }
    }

    @Override
    public Bar unmarshal(Object v) throws Exception {
        Bar bar = new Bar();
        Element element = (Element) v;
        bar.value = element.getTextContent();
        return bar;
    }

    @Override
    public Object marshal(Bar v) throws Exception {
        Document document = documentBuilder.newDocument();
        Element root = document.createElement("bar");
        root.setTextContent(v.value);
        return root;
    }

}

Java 模型

Foo

@XmlJavaTypeAdapter 注解用于引用XmlAdapter

import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Foo {

    @XmlJavaTypeAdapter(BarAdapter.class)
    private Bar bar;

}

条形

import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
public class Bar {

    String value;

}

演示代码

演示

由于创建 DocumentBuilderFactory 是有成本的,我们可以通过在 Marshaller 上设置一个实例来利用 JAXB 处理 XmlAdapter 的有状态实例的能力。

import java.io.File;
import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Foo.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/forum18272059/input.xml");
        Foo foo = (Foo) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setAdapter(new BarAdapter());
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(foo, System.out);
    }

}

input.xml/Output

<?xml version="1.0" encoding="UTF-8"?>
<foo>
    <bar>Hello World</bar>
</foo>

【讨论】:

  • 说我需要做的就是将我的@XmlAdapter 签名从Element 更改为Object 有点冗长:-P 当我在代码和回复你,谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-21
  • 2012-10-21
  • 2017-06-05
  • 1970-01-01
  • 1970-01-01
  • 2012-06-22
相关资源
最近更新 更多