JAXBgenerics@XmlValue

目标是使用JAXB生成以下XML

 string data binary data  

是否有允许通用 @XmlValue字段的解决方法(我需要存储byte[]String数据)? 以下是我的意愿:

 @XmlRootElement public class Foo { private @XmlElement List bars; } @XmlRootElement public class Bar { private @XmlValue T value; // (*) } 

但我得到了这个例外

(*)IllegalAnnotationException:
@XmlAttribute / @ XmlValue需要引用映射到XML文本的Java类型。

您可以在此用例中使用@XmlValue而不是@XmlValue

BarAdapter

 package forum8807296; import javax.xml.bind.annotation.adapters.XmlAdapter; public class BarAdapter extends XmlAdapter> { @Override public Bar unmarshal(Object v) throws Exception { if(null == v) { return null; } Bar bar = new Bar(); bar.setValue(v); return bar; } @Override public Object marshal(Bar v) throws Exception { if(null == v) { return null; } return v.getValue(); } } 

XmlAdapter使用@XmlJavaTypeAdapter注释与bars属性相关联:

 package forum8807296; import java.util.List; import javax.xml.bind.annotation.*; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; @XmlRootElement public class Foo { private List bars; @XmlElement(name="bar") @XmlJavaTypeAdapter(BarAdapter.class) public List getBars() { return bars; } public void setBars(List bars) { this.bars = bars; } } 

酒吧

 package forum8807296; public class Bar { private T value; public T getValue() { return value; } public void setValue(T value) { this.value = value; } } 

演示

您可以使用以下演示代码测试此示例:

 package forum8807296; import java.util.ArrayList; import java.util.List; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; public class Demo { public static void main(String[] args) throws Exception { JAXBContext jc = JAXBContext.newInstance(Foo.class); Foo foo = new Foo(); List bars = new ArrayList(); foo.setBars(bars); Bar stringBar = new Bar(); stringBar.setValue("string data"); bars.add(stringBar); Bar binaryBar = new Bar(); binaryBar.setValue("binary data".getBytes()); bars.add(binaryBar); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(foo, System.out); } } 

产量

请注意输出如何包含xsi:type属性以保留值的类型。 您可以通过让XmlAdapter返回String而不是Object来消除xsi:type属性,如果这样做,您将需要自己处理从String到适当类型的转换以进行解组操作:

   string data YmluYXJ5IGRhdGE=  

我无法让@XmlValue工作,因为我总是遇到NullPointerException – 不知道为什么。 我想出了类似下面的内容。

完全删除Bar类,因为,您希望它能够包含任何内容,您可以使用Object代表它。

 @XmlRootElement(name = "foo", namespace = "http://test.com") @XmlType(name = "Foo", namespace = "http://test.com") public class Foo { @XmlElement(name = "bar") public List bars = new ArrayList<>(); public Foo() {} } 

在没有告诉JAXB您的类型正在使用哪个名称空间的情况下, foo每个bar元素都包含单独的名称空间声明和填充 – package-info.java和所有名称空间内容仅用于特定目的。

 @XmlSchema(attributeFormDefault = XmlNsForm.QUALIFIED, elementFormDefault = XmlNsForm.QUALIFIED, namespace = "http://test.com", xmlns = { @XmlNs(namespaceURI = "http://test.com", prefix = ""), @XmlNs(namespaceURI = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi"), @XmlNs(namespaceURI = "http://www.w3.org/2001/XMLSchema", prefix = "xs")}) package test; import javax.xml.bind.annotation.XmlNs; import javax.xml.bind.annotation.XmlNsForm; import javax.xml.bind.annotation.XmlSchema; 

运行这个简单的测试会传出类似于XML片段的内容。

 public static void main(String[] args) throws JAXBException { JAXBContext context = JAXBContext.newInstance(Foo.class); Foo foo = new Foo(); foo.bars.add("a"); foo.bars.add("b".getBytes()); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); marshaller.marshal(foo, System.out); } 

输出:

   a Yg==  

有没有理由不用你的byte []简单地构造一个String? 你真的需要通用吗?

我通常使用的技巧是使用您想要的类型创建模式,然后使用xjc生成Java类并查看如何使用注释。 :)我相信在XML模式中,byte []的正确类型映射是’base64Binary’,所以创建这样的模式:

     

并运行xjc我们将得到以下代码生成:

 @XmlElementDecl(namespace = "http://www.example.org/NewXMLSchema", name = "aTest") public JAXBElement createATest(byte[] value) { return new JAXBElement(_ATest_QNAME, byte[].class, null, ((byte[]) value)); }