用于任意XML的JAXB @XmlAdapter [英] JAXB @XmlAdapter for arbitrary XML
问题描述
我有一个 org.w3c.dom.Element
,我从我的 XmlAdapter
返回自定义 @XmlElement
,我想把它作为JAXB对象的一部分包含为任意的XML(我知道我必须手工制作XSD)。但是,JAXB抱怨
I have a org.w3c.dom.Element
that I'm returning from my XmlAdapter
for a custom @XmlElement
and I'd like to include it as part of a JAXB object as arbitrary XML (I'm aware I'll have to hand-craft the XSD). However, JAXB complains with
org.w3c.dom.Element is an interface, and JAXB can't handle interfaces.
显然w3c的XML类型是不支持Java类型,这是一个耻辱。但是进一步的是,当我使用 javax.xml.transform.Result
时,我得到了相同的错误,这显然被支持。
Apparently the w3c XML types are not supported as Java types, which is a shame. But further than this, I get the same error when I use javax.xml.transform.Result
which is apparently supported.
如何在JAXB中包含任意的XML元素作为元素?
注意:根据 https://forums.oracle.com/thread/1668210 我也试过
MessageFactory factory = MessageFactory.newInstance();
message = factory.createMessage();
SOAPElement element = message.getSOAPBody().addDocument(doc);
但这也是同样的错误。
推荐答案
TL; DR
你可以有一个 XmlAdapter
将您的域对象转换为 org.w3c.dom.Element
的实例,只要将值类型指定为 Object
(不是元素
)。
TL;DR
You can have an XmlAdapter
that converts you domain object to an instance of org.w3c.dom.Element
as long as you specify the value type as Object
(not Element
).
下面是一个完整的例子
类型为的字段/属性java.lang.Object
将保留未知内容作为DOM节点。您可以通过在 XmlAdapter
中将值类型指定为 Object
来充分利用此用例。您将需要确保从 marshal
方法返回的根元素与 @XmlElement
注释。
A field/property of type java.lang.Object
will keep unknown content as DOM nodes. You can leverage this in your use case by specifying the value type in your XmlAdapter
as Object
. You will need to ensure that the root element returned from the marshal
method matches the field/property as defined by the @XmlElement
annotation.
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 Model
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有一定代价,我们可以利用JAXB的能力来处理XmlAdapter的有状态实例,方法是设置在Marshaller上的一个实例。
Since there is a cost to creating the DocumentBuilderFactory we can leverage JAXB's ability to handle stateful instances of XmlAdapter by setting an instance on the Marshaller.
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 /输出
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<bar>Hello World</bar>
</foo>
这篇关于用于任意XML的JAXB @XmlAdapter的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!