В прошлой статье мы познакомились с простыми
вещами, в этой усложним наши примеры, расмотрим
коллекции и так приступим. Возьмем пример из предыдущей статьи и создадим
класс Vit2:
@XmlRootElement(name
= "vit2")
@XmlAccessorType(XmlAccessType.FIELD)
public class Vit2 {
@XmlValue()
protected String name;
@XmlAttribute(name =
"date")
protected Date date;
public String getName() {
return name;
}
public void setName(String
value) {
this.name = value;
}
public Date getDate() {
return date;
}
public void setDate(Date
value) {
this.date
= value;
}
}
Вывод этого
класса, будет таким:
<?xml
version="1.0" encoding="UTF-8"
standalone="yes"?>
<vit2
date="2016-03-28T21:36:27.046+03:00">Vit</vit2>
Мы хотим получить коллекцию однотипных
тегов <vit2…/> , для этого напишем класс Vit3, который будет содержать коллекцию классов Vit2, для этого нам нужно пометить аннотацией @XmlElement коллекцию
List:
@XmlRootElement()
@XmlAccessorType(value = XmlAccessType.FIELD)
public class Vit3 {
@XmlElement
private List Vit1s;
public List getVit1s() {
return Vit1s;
}
public void setVit1s(List
vit1s) {
Vit1s = vit1s;
}
}
Напишем главный класс MainVit1,
который создает коллекцию и два элемента:
public class MainVit1 {
static public void main(String
args[]) {
Vit3 vit3 = new Vit3();
List v = new ArrayList();
Vit2 vit1 = new Vit2();
vit1.setName("Vit");
vit1.setDate(new Date());
v.add(vit1);
Vit2 vit2 = new Vit2();
vit2.setName("Vit1");
vit2.setDate(new Date());
v.add(vit2);
vit3.setVit1s(v);
try {
File file = new
File("vit.xml");
JAXBContext
jaxbContext = JAXBContext.newInstance(Vit3.class,Vit2.class);
Marshaller marshaller
= jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,true);
marshaller.marshal(vit3, file);
marshaller.marshal(vit3, System.out);
} catch (JAXBException e)
{
e.printStackTrace();
}
}
}
В итоге
получим такой вывод из двух тегов <Vit1s ../>:
<?xml
version="1.0" encoding="UTF-8"
standalone="yes"?>
<vit3>
<Vit1s
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="vit2"
date="2016-04-14T21:42:55.190+03:00">Vit</Vit1s>
<Vit1s
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="vit2"
date="2016-04-14T21:42:55.190+03:00">Vit1</Vit1s>
</vit3>
Усложним наш
пример, мне не нравиться, как выводится
тип Date, напишем Adapter который будет преобразовывать наш тип,
в нужный нам формат вывода. Сделаю небольшое отступление adapter нужен для преобразования сложных типов данных в нужный нам тег, и
наоборот из сложного xml тега преобразовать в нужный нам
тип данных java. В предыдущей статье я затронул тему,
преобразования простых типов java в типы xml схемы <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">, сдесь покажу, как преобразовывать специфические типы данных. И так у нас есть класс XmlAdapter, и его два метода, marshal и unmarshal которые помогают преобразовывать
входящий и исходящий объект в нужный нам формат данных. У класса нужно
переопределить типы данных которые мы будем преобразовывать, и так он имеет
такой вид XmlAdapter<Тип данных возвращаемый из xml
файла - всегда String, Тип данных
преобразуемый для Java>. В методах marshal и unmarshal прописываем преобразование и они
имеют шаблонный вид: Тип данных преобразуемый для Java unmarshal(Тип данных возвращаемый из xml
файла - всегда String) и Тип данных возвращаемый из xml
файла - всегда String marshal(Тип данных преобразуемый для Java) . Для того что бы все понять, напишем
класс DateAdapter:
import javax.xml.bind.annotation.adapters.XmlAdapter;
import
java.text.SimpleDateFormat;
import
java.util.Date;
public class
DateAdapter extends XmlAdapter<String,Date> {
@Override
public Date unmarshal(String v) throws Exception {
SimpleDateFormat sdf = new
SimpleDateFormat("dd.MM.YYYY");
return sdf.parse(v);
}
@Override
public String marshal(Date v) throws Exception {
SimpleDateFormat sdf = new
SimpleDateFormat("dd.MM.YYYY");
return sdf.format(v);
}
}
Осталось
последнее пометить тип данных, к которому будет применяться преобразование
посредством нашего класса адаптера, это делается посредством аннотации @XmlJavaTypeAdapter(),
и ей передается параметр наш класс адаптер. И так применим эту аннотацию к
классу Vit2:
@XmlRootElement(name = "vit2")
@XmlAccessorType(XmlAccessType.FIELD)
public class
Vit2 {
@XmlValue()
protected String name;
@XmlAttribute(name = "date")
@XmlJavaTypeAdapter(DateAdapter.class)
protected Date date;
….
Getter & Setter
….
}
В итоге получим такой
вывод в консоли:
<?xml version="1.0"
encoding="UTF-8" standalone="yes"?>
<vit3>
<Vit1s
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="vit2" date="15.04.2016">Vit</Vit1s>
<Vit1s
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="vit2" date="15.04.2016">Vit1</Vit1s>
</vit3>
Мы
избавились с помощью адаптера от такого некрасивого вывода даты, пометил желтым
цветом:
<Vit1s xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="vit2" date="2016-04-14T21:42:55.190+03:00">Vit1</Vit1s>
Так далее мы познакомимся с аннотацией @XmlElementWrapper() которая
помогает обернуть любой объект, в тег который вы укажете в параметре name, добавим аннотацию к нашему примеру:
@XmlRootElement()
@XmlAccessorType(value = XmlAccessType.FIELD)
public class Vit3 {
@XmlElement
@XmlElementWrapper(name =
"ListVit1")
private List Vit1s;
public List getVit1s() {
return Vit1s;
}
public void setVit1s(List
vit1s) {
Vit1s = vit1s;
}
}
И так получим на выходе:
<?xml version="1.0" encoding="UTF-8"
standalone="yes"?>
<vit3>
<ListVit1>
<Vit1s
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="vit2" date="20.04.2016">Vit</Vit1s>
<Vit1s
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="vit2" date="20.04.2016">Vit1</Vit1s>
</ListVit1>
</vit3>
Так в прошлой статье мы генерировали java классы, сейчас я вас познакомлю с
обратным процессом, как из класса помеченного аннотациями JAXB, сгенерировать xml схему. В этом поможет нам утилита schemagen, она входит
в поставку вашего JDK, давайте сгенерируем наш java класс Vit3.java. перейдите в каталог где находится ваш исходник, наберите такую строчку
в командной строке:
#/ schemagen -classpath . Vit3.java
На выходе вы получите файл schema1.xsd:
<?xml version="1.0" encoding="UTF-8"
standalone="yes"?>
<xs:schema version="1.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element
name="vit3" type="vit3"/>
<xs:complexType
name="vit3">
<xs:sequence>
<xs:element
name="ListVit1" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element
name="Vit1s" type="xs:anyType" minOccurs="0"
maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>
В связи с тем, что много различий между типами данных XML Schema
и Java
типами, не возможно просто соотнести интерфейсы java на типы xml схемы. Для этого придумали следующие
аннотации @XmlElements, @XmlAnyElement которые помогают нам работать с java интерфейсами. И так расмотрим их
применение, для этого напишем интерфейс и два класса которые будут
наследоваться от него. Напишем интерфейс
и два метода которые будут возвращать/устанавливать
номер телефона:
public interface Test {
void setTel(int Tel);
int getTel();
}
Реализуем класс домашний телефон и унаследуем наш интефейс:
@XmlRootElement
public class homeTel implements Test {
private int tel;
@Override
public void setTel(int Tel) {
tel = Tel;
}
@Override
public int getTel() {
return tel;
}
}
Реализуем следующий класс рабочий телефон:
@XmlRootElement
public class WorkTel implements Test
{
private int tel;
@Override
public void setTel(int Tel) {
tel = Tel;
}
@Override
public int getTel() {
return tel;
}
}
Применим аннотацию @XmlElements к коллекции, она содержит в себе
аннотации @XmlElement где вы указываете в параметре name имя тега в какой нужно обернуть
класс указанный в параметре type:
@XmlRootElement
@XmlAccessorType(value = XmlAccessType.FIELD)
public class Vit4 {
@XmlElements({
@XmlElement(name = "homeTel", type = homeTel.class),
@XmlElement(name = "workTel", type = WorkTel.class)
})
private List<Test>
contacts = new ArrayList<>();
public List<Test>
getContacts() {
return contacts;
}
public void
setContacts(List<Test> contacts) {
this.contacts = contacts;
}
}
Напишем
главный класс MainVit1, который создает коллекцию и два элемента:
public class MainVit1 {
static public void main(String
args[]) {
Vit4 vit4 = new Vit4();
Test t1 = new homeTel();
t1.setTel(12345);
Test t2 = new WorkTel();
t2.setTel(543678);
List<Test> L = new
ArrayList<>();
L.add(t1);
L.add(t2);
vit4.setContacts(L);
try {
File file = new
File("vit.xml");
JAXBContext
jaxbContext = JAXBContext.newInstance(Vit4.class);
Marshaller marshaller
= jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,true);
marshaller.marshal(vit4, System.out);
} catch (JAXBException e)
{
e.printStackTrace();
}
}
}
На выходе получим такой вывод:
<?xml version="1.0" encoding="UTF-8"
standalone="yes"?>
<vit4>
<homeTel>
<tel>12345</tel>
</homeTel>
<workTel>
<tel>543678</tel>
</workTel>
</vit4>
Теперь давайте поработаем с аннотацией @XmlAnyElement, применим её к нашей коллекции
вместо аннотации @XmlElements, напишем новый класс, эффект будет тот
же, вы можете добавлять в коллекцию любой класс реализующий интерфейс Test.
@XmlRootElement
@XmlAccessorType(value = XmlAccessType.FIELD)
public class Vit5 {
@XmlAnyElement
private List<Test>
contacts = new ArrayList<>();
public List<Test>
getContacts() {
return contacts;
}
public void
setContacts(List<Test> contacts) {
this.contacts = contacts;
}
}
Напишем
главный класс MainVit1, который создает коллекцию и два элемента:
public class MainVit1 {
static public void main(String
args[]) {
Vit5 vit5 = new Vit5();
Test t1 = new homeTel();
t1.setTel(12345);
Test t2 = new WorkTel();
t2.setTel(543678);
List<Test> L = new
ArrayList<>();
L.add(t1);
L.add(t2);
vit5.setContacts(L);
try {
File file = new
File("vit.xml");
JAXBContext
jaxbContext = JAXBContext.newInstance(Vit5.class);
Marshaller marshaller
= jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,true);
marshaller.marshal(vit5,
System.out);
} catch (JAXBException e)
{
e.printStackTrace();
}
}
}
На выходе получим такой вывод, практически не отличимый от предыдущего
вывода:
<?xml version="1.0" encoding="UTF-8"
standalone="yes"?>
<vit5>
<homeTel>
<tel>12345</tel>
</homeTel>
<workTel>
<tel>543678</tel>
</workTel>
</vit5>продолжение часть 3