4.12.2016

Web Service - JAXB, что нужно знать …



В этой статье поговорим о JAXB ( Java Architecture for XML Binding ), это стандарт который помогает нам быстро преобразовывать xml файлы в объекты POJO, этот процесс называют демаршалингом ( Unmarshalling ), и делать обратное преобразование из объектов POJO  в xml файлы, его еще называют маршалингом ( Marshalling ).  Во всех преобразованиях, нам помогают аннотации jaxb, которые мы применяем к объекту POJO,  в принципе мы можем, без каких либо проблем использовать вместе аннотации  JPA и JAXB, что позволяет нам, например из баз данных получать на прямую xml данные и наоборот, представьте какая мощь заключена в этой библиотеке. И так, эта  библиотека уже включена в поставку вашего jdk, с 6 версии, давайте узнаем версию нашей библиотеки, выполним команду:

C:\Users\kamaz1>xjc -version
xjc 2.2.8-b130911.1802

Для начала напишем простой пример, маршалинга и демаршалинга и потом будем постепенно усложнять наш пример и учится более сложным вещам. И так для того что бы мы смогли делать преобразования, нам понадобиться объект POJO, пометим его аннотациями:

package com.vit.fly;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.Serializable;
import java.util.Date;

@XmlRootElement()//корневой тег  xml  документа
public class Vit1  implements Serializable{
    public String name;
    public Date date;

    public String getName() {
        return name;
    }

    @XmlElement()//вложенный тег в корневой тег xml документа
    public void setName(String name) {
        this.name = name;
    }

    public Date getDate() {
        return date;
    }

    @XmlAttribute//атрибут корневого тега
    public void setDate(Date date) {
        this.date = date;
    }

   @Override
    public String toString() {
        return new String("[ name= "+name+", date= "+date.toString()+" ]");
    }
}

Напишем с начала преобразование из объекта в xml  документ:

package com.vit.fly;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.File;
import java.util.Date;

public class MainVit1 {
    static public void main(String args[]){
        Vit1 vit1 = new Vit1();
        vit1.setName("Vit");
        vit1.setDate( new Date());
        try {
            File file = new File("vit.xml");
            JAXBContext jaxbContext = JAXBContext.newInstance(Vit1.class);//создаем контекст jaxb
            Marshaller marshaller = jaxbContext.createMarshaller();//получаем marshaller
            marshaller.marshal(vit1,file);//создаем xml файл
            marshaller.marshal(vit1,System.out);//выводим xml преобразование
        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }
}

Получили такой xml файл:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<vit1 date="2015-12-25T21:13:27.465+03:00">
<name>Vit</name>
</vit1>

Напишем обратное преобразование из нашего xml  документа в pojo  объект:

package com.vit.fly;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.File;

public class MainVit2 {
    static public void main(String args[]){
        try {
            File file = new File("vit.xml");
            JAXBContext jaxbContext = JAXBContext.newInstance(Vit1.class);//создаем контекст jaxb
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();//получаем unmarshaller
            Vit1 vit1 = (Vit1)unmarshaller.unmarshal(file);//получаем pojo  объект из xml файла
            System.out.println(vit1);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }
}

Получили такой вывод:

[ name= Vit, date= Fri Dec 25 21:13:27 GMT+03:00 2015 ]


Добавим поле lastName и установим ему значение, затем попробуем сформировать xml файл:

@XmlRootElement()
public class Vit1  implements Serializable{
    private String name;
    public String lastName;
    private Date date;

public class MainVit1 {
    static public void main(String args[]){
        Vit1 vit1 = new Vit1();
        vit1.lastName = "Lopanov";
        vit1.setName("Vit");
        vit1.setDate( new Date());
        try {
            File file = new File("vit.xml");
            JAXBContext jaxbContext = JAXBContext.newInstance(Vit1.class);//создаем контекст jaxb
            Marshaller marshaller = jaxbContext.createMarshaller();//получаем marshaller
            marshaller.marshal(vit1,file);//создаем xml файл
            marshaller.marshal(vit1,System.out);//выводим xml преобразование
        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }
}

На выходе получили такой файл:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<vit1 date="2015-12-27T18:53:08.767+03:00">
<lastName>Lopanov</lastName>
<name>Vit</name>
</vit1>

Этот пример показывает, что по умолчанию  jaxb преобразует все public поля и методы доступа в теги, это поведение можно переопределить с помощью аннотации @XmlAccessorType(). Значение XmlAccessType.PUBLIC_MEMBER – применимо по умолчанию, XmlAccessType.FIELD – преобразует все имеющиеся поля вне зависимости от методов доступа поля класса, XmlAccessType.PROPERTY – преобразует, все поля класса, у кого есть методы доступа set и get (сеттеры и геттеры). Если нужно не включать ни какие поля класса в преобразования, выберите  XmlAccessType.NONE, применяется обычно к зависимым объектам, которые не нужно преобразовывать в xml теги. Предыдущий пример можно переписать так, добавив аннотацию  @XmlAccessorType():

@XmlRootElement()                                                                
@XmlAccessorType(value = XmlAccessType.PUBLIC_MEMBER)//значение по умолчанию
public class Vit1  implements Serializable{
    private String name;
    public String lastName;
    ….

Если вы примените аннотацию к любому полю @XmlTransient, то такое поле будет исключено из преобразования в xml.

@XmlRootElement()
public class Vit1  implements Serializable{
    private String name;
    @XmlTransient // поле не отобразится в xml
    public String lastName;
    ….

Вывод на выходе:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<vit1 date="2015-12-27T18:53:08.767+03:00">
<name>Vit</name>
</vit1>

По умолчанию теги в xml файл выгружаются без сортировки по алфавиту,  в какой последовательности вы опишите поля в классе, так они и выведутся в файл, если у вас много тегов, примените аннотацию @XmlAccessorOrder и укажите в нем параметр XmlAccessOrder.ALPHABETICAL, тогда ваши теги будут отсортированы в алфавитном порядке. Для примера уберем на время аннотацию @XmlAttribute для наглядности:

@XmlRootElement()
@XmlAccessorOrder(value = XmlAccessOrder.ALPHABETICAL)
public class Vit1  implements Serializable{
    private String name;
    public String lastName;
    private Date date;

Даст нам такой вывод:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<vit1>
<date>2016-03-21+03:00</date>
<lastName>Lopanov</lastName>
<name>Vit</name>
</vit1>

Вернемся к аннотации @XmlRootElement, параметр name  задает имя тега, параметр namespace указывает пространство имен, например:

@XmlRootElement(name = "VitRoot",namespace = "Hello/Vit")
public class Vit1  implements {
    private String name;
    public String lastName;
    private Date date;
….
 }

преобразуется в такой xml код:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:VitRoot xmlns:ns2="Hello/Vit">
<name>Vit</name>
<lastName>Lopanov</lastName>
<date>2016-03-21+03:00</date>
</ns2:VitRoot>

Перейдем к рассмотрению аннотации @XmlElement(), как она работает, как преобразует java типы данных в xml? Ему помогает схема xml документа, как вы помните, в схеме документа описываются типы полей, нашего xml файла.  Наша аннотация @XmlElement() непосредственно трансформируется в тег схемы <xs:element name="имя поля класса" /> значение поля класса </xs:element>.  Напишем схему нашего “pojo объекта” vit1.xsd:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://com.vit"
           xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:vi="http://com.vit"
           elementFormDefault="qualified"
        >
    <xs:element name="VitRoot" type="vi:Vit1"/>
    <xs:complexType name="Vit1">
        <xs:sequence>
            <xs:element name="name" type="xs:string"/>
            <xs:element name="lastName" type="xs:string"/>
        </xs:sequence>
        <xs:attribute name="date" type="xs:date"/>
    </xs:complexType>
</xs:schema>

Преобразуем в java классы посредством утилиты xjc,  зададим параметры –d директория, где будут создаваться наши java классы, -p java пакет, создайте каталог  в текущей директории out, выполните команду:

C:\jboss-4.2.0.GA\vit\jboss_ejb\src>xjc -d out -p com.vit.fly vit1.xsd
parsing a schema...
compiling a schema...
com\vit\fly\ObjectFactory.java
com\vit\fly\Vit1.java
com\vit\fly\package-info.java

рассмотрим файл Vit1.java, посмотрим что получилось:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Vit1", propOrder = {    "name",    "lastName"})
public class Vit1 {

    @XmlElement(required = true)
    protected String name;
    @XmlElement(required = true)
    protected String lastName;
    @XmlAttribute(name = "date")
    @XmlSchemaType(name = "date")
    protected XMLGregorianCalendar date;

…..
убрал getter & setter полей

}

Осталось добавить только, аннотацию @XmlRootElement  и у нас готов класс к работе, таким образом, имея схему нашего xml документа, мы можем сгенерировать классы, которые будут работать с JAXB. Обратите внимание у нас появился новый тег @XmlType(name = "Vit1", propOrder = {    "name",    "lastName"}), где параметр name соответствует имени типа <xs:complexType name="Vit1">, а параметр propOrder показывает порядок вывода тегов в xml файле, еще один метод “сортировки тегов”. Так же с классом Vit1.java, автоматически сформировались два файла package-info.java и ObjectFactory.java, об ObjectFactory.java я вам расскажу позже, а сейчас давайте поговорим о файле package-info.java. И так зачем он нужен, у каждого “приличного” xml файла должно быть пространство имен, его можно задать четырьмя способами. Первый способ, посредством аннотации  @XmlRootElement(name = "VitRoot",namespace = "Hello/Vit"), я вам его уже показывал в одном из примеров.  Второй способ с помощью аннотации, которая применяется к пакету класса XmlSchema() и её обычно применяют к пакету в файле package-info.java, рассмотрим содержимое этого файла:

@javax.xml.bind.annotation.XmlSchema(namespace = "http://com.vit", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.vit.fly.remoteejb;

в параметре namespace   задается пространство имен по умолчанию,  файл package-info.java должен находиться вместе с классом Vit1.java в одной директории. И так обратите внимание, у нас в выводе появился атрибут xmlns, пространство имен по умолчанию для тега vit1:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<vit1 xmlns="http://com.vit">
<name>Vit</name>
<lastName>Lopanov</lastName>
<date>2016-03-22+03:00</date>
</vit1> 

Третий способ это в аннотации @XmlType() задать параметр namespace, который имеет больший приоритет, чем в аннотации XmlSchema(), взглянем на пример:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "vit1",namespace = "http://com.vit1",propOrder = {"name", "date","lastName"})
public class Vit1 {

    @XmlElement(required = true)
    protected String name;
    @XmlElement(required = true)
    protected String lastName;
    //@XmlAttribute(name = "date")
    @XmlSchemaType(name = "date")
    protected Date date;
}

Посмотрим на вывод, что изменилось с прошлого примера, какой эффект дала нам эта аннотация:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:vit1 xmlns="http://com.vit1" xmlns:ns2="http://com.vit">
<name>Vit</name>
<date>2016-03-23+03:00</date>
<lastName>Lopanov</lastName>
</ns2:vit1>

И последняя четвертая возможность, если у вас есть потребность, то можно задать параметр namespace на уровне аннотаций @XmlElement() и @XmlAttribute(), т. е. применить к атрибуту или к тегу конкретное пространство имен, расмотрим пример:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "vit1",namespace = "http://com.vit1",propOrder = {"name", "date","lastName"})
//@XmlAccessorOrder(value = XmlAccessOrder.ALPHABETICAL)
public class Vit1 {

    @XmlElement(required = true)
    protected String name;
    @XmlElement(required = true,namespace = "http://com.vit1.element")
    protected String lastName;
    @XmlAttribute(name = "date",namespace = "http://com.vit1.attribute")
    //@XmlSchemaType(name = "date")
    protected Date date;

}

И вот какой вывод xml файла мы получили:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns4:vit1 xmlns="http://com.vit1" xmlns:ns2="http://com.vit1.attribute" xmlns:ns3="http://com.vit1.element"   xmlns:ns4="http://com.vit" ns2:date="2016-03-23T22:14:11.698+03:00">
<name>Vit</name>
<ns3:lastName>Lopanov</ns3:lastName>
</ns4:vit1>

Нужно расказать еще об одной вещи,  это про префикс, его можно заменить на нужный нам, делается это все спомощью аннотации XmlSchema() задается параметром xmlns, взглянем на наш пример:

@XmlSchema(xmlns = {@XmlNs(prefix = "v1", namespaceURI = "http://www.vit.com/CC")},elementFormDefault = XmlNsForm.QUALIFIED)
package com.vit.fly.remoteejb;

и так смотрим, что получилось в итоге:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<vit1 xmlns:v1="http://www.vit.com/vit" date="2016-03-24T21:42:52.749+03:00">
<name>Vit</name>
<lastName>Lopanov</lastName>
</vit1>

Давайте я вас познакомлю еще с одной интересной аннотацией @XmlValue(), её не заслуженно забывают.  Она устанавливает значение элемента, как мы знаем, у тега могут быть,  или вложенные теги, которые задаются аннотацией @XmlElement(), или только значение, которое задается аннотацией @XmlValue(). Напишем класс, используя нашу аннотацию:

@XmlRootElement(name = "vit2")
@XmlAccessorType(XmlAccessType.FIELD)
public class Vit2 {

    @XmlValue()
    protected String name;
    @XmlAttribute(name = "date")
    protected Date date;


}

Вывод этого класса, будет таким:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<vit2 date="2016-03-28T21:36:27.046+03:00">Vit</vit2>

Next

Комментариев нет:

Отправить комментарий