JAXB: XmlJavaTypeAdapter с XmlElementRef (type = JAXBElement) - PullRequest
1 голос
/ 04 января 2012

Вопрос относится к JAXB 2.1.10 в JDK 6

Со вчерашнего дня я пытаюсь выяснить способ определения @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, и я получил следующие сгенерированные файлы -

  1. package-info.java
  2. ObjectFactory.java
  3. ProductContext.java
  4. Product.java
  5. Item.java

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

, чтобы разрешить поиск элементов на основе имени элемента (хотя я не хочу менять класс продукта)

В настоящее время ProductContext класс получил ссылку на продукт, а не сам продукт (потому что: minOccurs = "0" & ​​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 -

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> и не Product

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

То, что я не люблю использовать это:

  • Мой адаптер тесно связан только с 1 JAXBElement (В данном случае: {"blah_blah"} продукт с областью действия ProductContext) Этот адаптер не будет работать с другими элементами того же типа Product

Я попытался улучшить Адаптер как -

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

Но это вызывает проблемы как во время демаршаллинга (нет населения на карте), так и Маршаллинг (выдает исключение - невозможно маршалировать тип «test.jaxb.Product» как элемент, потому что отсутствует аннотация @XmlRootElement)

Может кто-нибудь подсказать, как мне использовать @XmlJavaTypeAdapter с @XmlElementRef(type=JAXBElement) без Адаптера, возвращающего JAXBElement в качестве значения Тип ??

...