【问题标题】:JAXB generic @XmlValueJAXB 通用 @XmlValue
【发布时间】:2012-01-10 17:03:40
【问题描述】:

目标是使用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 类型。

【问题讨论】:

    标签: java xml generics jaxb marshalling


    【解决方案1】:

    对于这个用例,您可以使用XmlAdapter 而不是@XmlValue

    条形适配器

    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();
        }
    
    }
    

    Foo

    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<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 属性以保留值的类型。你可以通过让你的XmlAdapter返回String而不是Object来消除xsi:type属性,如果你这样做,你需要自己处理从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>
    

    【讨论】:

      【解决方案2】:

      我无法让@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 你的类型正在使用哪些命名空间,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 sn-p 的东西。

      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>
      

      【讨论】:

      • +1 - 这是一个示例,说明如何使用 XmlAdapter 做同样的事情,但保留 Bar 类:stackoverflow.com/a/8901997/383861
      • @BlaiseDoughan 感谢您的澄清!
      【解决方案3】:

      你有什么理由不简单地用你的 byte[] 构造一个字符串?你真的需要一个泛型吗?

      【讨论】:

      • 这需要我将不透明的二进制数据转换为字符串,例如,我必须手动将其编码为例如 hexBinary 或 base64。但是,是的,这就是我目前用作解决方法的方法。
      • 您使用的是自己的编码算法吗?如果您使用apache commons encoder,应该会很轻松。
      • 我正在使用 HexBinaryAdapter 哪个 alt。 commons base64 类,都是不错的单行代码。
      【解决方案4】:

      我通常使用的技巧是使用您想要的类型创建模式,然后使用 xjc 生成 Java 类并查看如何使用注释。 :) 我相信 byte[] 的 XML 模式正确类型映射是“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));
      }
      

      【讨论】:

        猜你喜欢
        • 2013-03-07
        • 1970-01-01
        • 2013-03-03
        • 1970-01-01
        • 1970-01-01
        • 2018-02-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多