Статьи

Отображение произвольного списка объектов с использованием @XmlAnyElement и XmlAdapter JAXB

@XmlAnyElement аннотации позволяют свойство обрабатывать произвольные элементы XML, а XMLAdapter обеспечивает способ преобразовать объект , который не может быть отображен в один , что может. В этом посте мы объединим эти два механизма, чтобы отобразить список произвольных объектов.

Этот пост будет охватывать следующие концепции:

  1. @XmlAnyElement аннотаций
  2. Уровень типа XmlAdapter
  3. Маршаллинг / демаршаллинг корневых уровней простых типов данных (например, String и Integer )
  4. Указание корневого элемента через JAXBElement
  5. Указание типа, который нужно разобрать на Unmarshaller

XML

Ниже приведен XML, который мы будем использовать для этого примера. Он представляет вызов метода и включает в себя имя метода и значения параметров. Ключевым моментом является то, что имя параметра представлено именем элемента, и эти имена элементов не известны заранее.

<?xml version="1.0" encoding="UTF-8"?>
<method name="addCustomer">
    <id type="java.lang.Integer">123</id>
    <name type="java.lang.String">Jane Doe</name>
    <address type="blog.anyelement.adapted.Address">
        <street>123 A Street</street>
        <city>Any Town</city>
    </address>
</method>

Модель Java

Вот модель Java, которую мы будем использовать для этого поста.

метод

Это корневой объект для нашей доменной модели. Мы не знаем имен и типов всех элементов, которые будут соответствовать свойству параметров , поэтому мы будем пометить его @XmlAnyElement .

package blog.anyelement.adapted;

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

@XmlRootElement
public class Method {

    private String name;
    private List& lt;Parameter> parameters;

    @XmlAttribute
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @XmlAnyElement
    public List<Parameter> getParameters() {
        return parameters;
    }

    public void setParameters(List<Parameter> parameters) {
        this.parameters = parameters;
    }

}

параметр

Наши параметры имеют имя и значение. Поскольку нам нужно будет адаптировать все экземпляры параметра , мы укажем тип XmlAdapter с помощью аннотации @XmlJavaTypeAdapter .

package blog.anyelement.adapted;

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

@XmlJavaTypeAdapter(ParameterAdapter.class)
public class Parameter {

    private String name;
    private Object value;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }

}

Адрес

Это пример объекта домена, который может быть установлен в качестве значения параметра.

package blog.anyelement.adapted;

public class Address {

    private String street;
    private String city;

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

}

ParameterAdapter

В этом XmlAdapter мы преобразуем экземпляр Parameter в элемент DOM, который может обрабатываться свойством, сопоставленным с @XmlAnyElement .

Unmarshal Operation

  1. Прочитайте атрибут type, чтобы определить класс для объекта значения (строка 73).
  2. Разберите элемент DOM, используя один метод unmarshal, который принимает параметр класса (строка 78). Этот параметр сообщает реализации JAXB, что такое целевой класс. Нам нужно сделать это, поскольку мы не связывали (и не могли) этот локальный корневой элемент с классом, используя @XmlRootElement или @XmlElementDecl .
  3. Создайте экземпляр параметра, заполнив элемент DOM (строка 82) и JAXBElement (строка 83).

Маршал Операция

  1. Создайте QName для представления локального корневого элемента для экземпляра параметра  (строка 50).
  2. Создайте JAXBElement на основе QName , типа значения и объекта значения (строка 53).
  3. Маршал JAXBElement для элемента DOM (строка 58).
  4. Установите атрибут type для элемента DOM на основе типа объекта значения (строка 62).

package blog.anyelement.adapted;

import javax.xml.bind.*;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.namespace.QName;
import javax.xml.parsers.*;
import javax.xml.transform.dom.DOMSource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class ParameterAdapter extends XmlAdapter<Element, Parameter> {

    private ClassLoader classLoader;
    private DocumentBuilder documentBuilder;
    private JAXBContext jaxbContext;

    public ParameterAdapter() {
        classLoader = Thread.currentThread().getContextClassLoader();
    }

    public ParameterAdapter(JAXBContext jaxbContext) {
        this();
        this.jaxbContext = jaxbContext;
    }

    private DocumentBuilder getDocumentBuilder() throws Exception {
        // Lazy load the DocumentBuilder as it is not used for unmarshalling.
        if (null == documentBuilder) {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            documentBuilder = dbf.newDocumentBuilder();
        }
        return documentBuilder;
    }

    private JAXBContext getJAXBContext(Class<?> type) throws Exception {
        if (null == jaxbContext) {
            // A JAXBContext was not set, so create a new one based  on the type.
            return JAXBContext.newInstance(type);
        }
        return jaxbContext;
    }

    @Override
    public Element marshal(Parameter parameter) throws Exception {
        if (null == parameter) {
            return null;
        }

        // 1. Build the JAXBElement to wrap the instance of Parameter.
        QName rootElement = new QName(parameter.getName());
        Object value = parameter.getValue();
        Class<?> type = value.getClass();
        JAXBElement jaxbElement = new JAXBElement(rootElement, type, value);

        // 2.  Marshal the JAXBElement to a DOM element.
        Document document = getDocumentBuilder().newDocument();
        Marshaller marshaller = getJAXBContext(type).createMarshaller();
        marshaller.marshal(jaxbElement, document);
        Element element = document.getDocumentElement();

        // 3.  Set the type attribute based on the value's type.
        element.setAttribute("type", type.getName());
        return element;
    }

    @Override
    public Parameter unmarshal(Element element) throws Exception {
        if (null == element) {
            return null;
        }

        // 1. Determine the values type from the type attribute.
        Class<?> type = classLoader.loadClass(element.getAttribute("type"));

        // 2. Unmarshal the element based on the value's type.
        DOMSource source = new DOMSource(element);
        Unmarshaller unmarshaller = getJAXBContext(type).createUnmarshaller();
        JAXBElement jaxbElement = unmarshaller.unmarshal(source, type);

        // 3. Build the instance of Parameter
        Parameter parameter = new Parameter();
        parameter.setName(element.getLocalName());
        parameter.setValue(jaxbElement.getValue());
        return parameter;
    }

}

демонстрация

Приведенный ниже демонстрационный код загрузит XML в объекты, а затем упорядочит объекты обратно в XML. Мы создадим экземпляр ParameterAdapter на основе JAXBContext (строка 11) и установим его как для unmarshaller (строка 14), так и для marshaller (строка 19).

package blog.anyelement.adapted;

import java.io.File;
import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Method.class, Parameter.class,
                Address.class);
        ParameterAdapter adapter = new ParameterAdapter(jc);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        unmarshaller.setAdapter(adapter);
        File xml = new File("src/blog/anyelement/adapted/input.xml");
        Method action = (Method) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setAdapter(adapter);
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(action, System.out);
    }

}

Скачать исходный код

Исходный код этого поста размещен на GitHub здесь . Вы можете скачать исходный код в виде zip-файла здесь .

 

От http://blog.bdoughan.com/2012/02/xmlanyelement-and-xmladapter.html