JAXB:带有XmlElementRef的XmlJavaTypeAdapter(type = JAXBElement)

时间:2012-01-03 23:22:21

标签: xml jaxb xsd jaxb2

该问题与JDK 6中的JAXB 2.1.10相关

从昨天开始,我想弄清楚如何在一个字段上与@XmlJavaTypeAdapter一起定义XmlElementRef

从网上我有几个显示这种用法的例子,但它们都使用从值类型的@XmlRootElement生成的元素名称。 我的问题在于我有一个类型为JAXBElement<T>

的引用

为了澄清,让我提出方案。

架构(我无法控制)是这样的:

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
             xmlns:xs="http://www.w3.org/2001/XMLSchema"
            elementFormDefault="qualified" 
            attributeFormDefault="unqualified" 
            targetNamespace="blah_blah"
            xmlns:tns="blah_blah" >

<element name="productContext" type="tns:ProductContext" />

<complexType name="ProductContext">
    <sequence>
        <element name="product" type="tns:Product" minOccurs="0" maxOccurs="1" nillable="true" />
        <element name="owner" type="string" />
    </sequence>
</complexType>

<complexType name="Product">
    <sequence>
        <element name="item" type="tns:Item" minOccurs="0" maxOccurs="unbounded" />
    </sequence>
    <attribute name="id" type="int" use="optional" />
</complexType>

<complexType name="Item">
    <sequence>
        <element name="name" type="string" />
        <element name="category" type="string" />
        <element name="range" type="string" />
        <element name="price" type="double" minOccurs="0" nillable="true"/>
    </sequence>
</complexType>

</schema>

我通过XJC&amp; amp;编译了架构。我得到了以下生成的文件 -

  1. package-info.java
  2. ObjectFactory.java
  3. ProductContext.java
  4. Product.java
  5. Item.java
  6. 我要解组的XML就像这样 -

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <productContext xmlns="blah_blah">
          <product id="11">
               <item>
                <name>item_0</name>
                <category>category_0</category>
                <range>range_0</range>
                <price>300</price>
              </item>
              <item>
                <name>item_1</name>
                <category>category_1</category>
                <range>range_1</range>
                <price>400</price>
              </item>
         </product>
         <owner>Vikas</owner>
    </productContext>
    

    我可以将其解组为以下测试类:

    package test.jaxb;
    
    import java.io.FileReader;
    
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.JAXBElement;
    import javax.xml.bind.Unmarshaller;
    
    public class Test {
    
        public static void main(String... args) throws Exception {
    
            JAXBContext context = JAXBContext.newInstance(ProductContext.class);
            ObjectFactory factory = new ObjectFactory();
    
            Unmarshaller unmarshaller = context.createUnmarshaller();
            JAXBElement<ProductContext> productContext = (JAXBElement<ProductContext>) unmarshaller.unmarshal(new FileReader("C:\\test.xml"));
    
            Product product = productContext.getValue().getProduct().getValue();
    
            System.out.println("Product Id : " + product.getId());
            System.out.println("Item 0 : " + product.getItem().get(0).getName());
            System.out.println("Item 1 : " + product.getItem().get(1).getName());
        }
    }
    

    运行它会让我:

    Product Id : 11
    Item 0 : item_0
    Item 1 : item_1
    

    我的问题如下:

    ProductContext班, 我希望java.util.HashMap<String, Item>取代Product

    允许根据项目名称查找项目(我不想更改Product类)

    目前ProductContext课程已获得参考产品, 而不是产品本身 (因为:minOccurs =“0”&amp; nillable =“true”)如下 -

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "ProductContext", propOrder = {
        "product",
        "owner"
    })
    public class ProductContext {
    
        @XmlElementRef(name = "product", namespace = "blah_blah", type = JAXBElement.class)
        protected JAXBElement<Product> product;
    
        ..
    

    }

    ObjectFactory看起来像:

    @XmlRegistry
    public class ObjectFactory {
    
        ..
    
        /**
         * Create an instance of {@link Product }
         * 
         */
        public Product createProduct() {
            return new Product();
        }
    
        /**
         * Create an instance of {@link JAXBElement }{@code <}{@link Product }{@code >}}
         * 
         */
        @XmlElementDecl(namespace = "blah_blah", name = "product", scope = ProductContext.class)
        public JAXBElement<Product> createProductContextProduct(Product value) {
            return new JAXBElement<Product>(_ProductContextProduct_QNAME, Product.class, ProductContext.class, value);
        }
    }
    

    我尝试引入名为ProductAdapter的适配器,如下面的

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "ProductContext", propOrder = {
        "product",
        "owner"
    })
    public class ProductContext {
    
        @XmlElementRef(name = "product", namespace = "blah_blah", type = JAXBElement.class)
        @XmlJavaTypeAdapter(test.jaxb.ProductAdapter.class)
        protected HashMap<String, Item> product;
    
        ..
    }
    

    &安培;适配器为:

    package test.jaxb;
    
    import java.util.HashMap;
    import java.util.Map.Entry;
    
    import javax.xml.bind.JAXBElement;
    import javax.xml.bind.annotation.adapters.XmlAdapter;
    
    public class ProductAdapter extends XmlAdapter<JAXBElement<Product>, HashMap<String, Item>> {
    
        @Override
        public HashMap<String, Item> unmarshal(JAXBElement<Product> valueType) throws Exception {
    
            HashMap<String, Item> map = new HashMap<String, Item>();
            Product product = valueType.getValue();
    
            for(Item item : product.getItem()) {
                map.put(item.getName() , item);
            }
    
            return map;
        }
    
        @Override
        public JAXBElement<Product> marshal(HashMap<String, Item> boundType) throws Exception {
            ObjectFactory factory = new ObjectFactory();
            Product product = factory.createProduct();
    
            for(Entry<String, Item> entry : boundType.entrySet()) {
                product.getItem().add(entry.getValue());
            }
    
            // return a JAXBElement of Product which is ProductContext Scoped 
            return factory.createProductContextProduct(product);
        }
    }
    

    我仍然可以使用Test class -

    解组它
    package test.jaxb;
    
    import java.io.FileReader;
    import java.util.Map.Entry;
    
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.JAXBElement;
    import javax.xml.bind.Unmarshaller;
    
    
    public class Test {
    
        public static void main(String... args) throws Exception {
    
            JAXBContext context = JAXBContext.newInstance(ProductContext.class);
            ObjectFactory factory = new ObjectFactory();
    
            Unmarshaller unmarshaller = context.createUnmarshaller();
            JAXBElement<ProductContext> productContext = (JAXBElement<ProductContext>) unmarshaller.unmarshal(new FileReader("C:\\test.xml"));
    
            /*
            Product product = productContext.getValue().getProduct().getValue();
    
            System.out.println("Product Id : " + product.getId());
            System.out.println("Item 0 : " + product.getItem().get(0).getName());
            System.out.println("Item 1 : " + product.getItem().get(1).getName());
            */
    
            for(Entry<String, Item> entry : productContext.getValue().getProduct().entrySet()) {
                System.out.println( "Item name : " + entry.getKey() + " , value : " + entry.getValue().getCategory());  
            }
        }
    }
    

    产生以下输出 -

    Item name : item_1 , value : category_1
    Item name : item_0 , value : category_0
    

    然而问题出在适配器的ValueType

    ProductAdapter的值类型为JAXBElement<Product>&amp;不是Product

    ProductAdapter extends XmlAdapter<JAXBElement<Product>, HashMap<String, Item>>
    

    我不喜欢使用它:

    • 我的适配器仅与1个JAXBElement紧密耦合 (在这种情况下:{“blah_blah”}产品具有ProductContext范围) 此适配器不适用于相同类型的其他元素Product

    我尝试将适配器增强为 -

    public class ProductAdapter extends XmlAdapter<Product, HashMap<String, Item>>
    

    但它在Unmarshalling(没有Map的人口)和&amp; 编组(给出异常 - 无法编组类型“test.jaxb.Product”作为元素,因为它缺少@XmlRootElement注释)

    有人可以指导我如何使用@XmlJavaTypeAdapter 没有Adapter的@XmlElementRef(type=JAXBElement)将JAXBElement作为值返回类型??

0 个答案:

没有答案