JAXB泛型@XmlValue

时间:2012-01-10 17:03:40

标签: java xml generics jaxb marshalling

目标是使用JAXB

生成以下XML
<foo>
   <bar>string data</bar>
   <bar>binary data</bar>
</foo>

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

@XmlRootElement
public class Foo {
    private @XmlElement List<Bar> bars;
}

@XmlRootElement
public class Bar<T> {
    private @XmlValue T value;  // (*)
}

但是我得到了这个例外

  

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

5 个答案:

答案 0 :(得分:8)

您可以针对此用例使用XmlAdapter而不是@XmlValue

<强> BarAdapter

package forum8807296;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class BarAdapter extends XmlAdapter<Object, Bar<?>> {

    @Override
    public Bar<?> unmarshal(Object v) throws Exception {
        if(null == v) {
            return null;
        }
        Bar<Object> bar = new Bar<Object>();
        bar.setValue(v);
        return bar;
    }

    @Override
    public Object marshal(Bar<?> v) throws Exception {
        if(null == v) {
            return null;
        }
        return v.getValue();
    }

}

<强>富

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

package forum8807296;

import java.util.List;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlRootElement
public class Foo {
    private List<Bar> bars;

    @XmlElement(name="bar")
    @XmlJavaTypeAdapter(BarAdapter.class)
    public List<Bar> getBars() {
        return bars;
    }

    public void setBars(List<Bar> bars) {
        this.bars = bars;
    }

}

<强>酒吧

package forum8807296;

public class Bar<T> {
    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<Bar> bars = new ArrayList<Bar>();
        foo.setBars(bars);

        Bar<String> stringBar = new Bar<String>();
        stringBar.setValue("string data");
        bars.add(stringBar);

        Bar<byte[]> binaryBar = new Bar<byte[]>();
        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属性以保留值的类型。您可以通过xsi:type返回XmlAdapter而不是String来消除Object属性,如果您这样做,则需要处理从String<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <foo> <bar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">string data</bars> <bar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:base64Binary">YmluYXJ5IGRhdGE=</bars> </foo> 的转换适用于解组操作的类型:

{{1}}

答案 1 :(得分:4)

我无法让@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<Object> bars = new ArrayList<>();

  public Foo() {}
}

在没有告诉JAXB您的类型使用哪个名称空间的情况下,bar中的每个foo元素都包含单独的名称空间声明和内容 - 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);
}

输出:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://test.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <bar xsi:type="xs:string">a</bar>
    <bar xsi:type="xs:base64Binary">Yg==</bar>
</foo>

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/NewXMLSchema" xmlns:tns="http://www.example.org/NewXMLSchema" elementFormDefault="qualified">
    <element name="aTest" type="base64Binary"></element>
</schema>

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

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

答案 4 :(得分:0)

https://stackoverflow.com/a/3074027/1851289可能是重复的,但答案还有其他两个选项。