Вопрос относится к 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, и я получил следующие сгенерированные файлы -
- package-info.java
- ObjectFactory.java
- ProductContext.java
- Product.java
- 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 в качестве значения Тип ??