JAXB пишем Hello World

В данном уроке будем знакомиться с таким инструментом как JAXB, где он используется и, естественно, рассмотрим все на простых примерах.

Шаг 0. Обзор

JAXB — Java Architecture for XML Binding. Специальный инструмент для маршалинга и анмаршалинга объектов. Часто используется в веб сервисах для представления объекта в виде XML схемы и передачи ее по сети. Также, такой способ передачи данных имеет свой плюс при обмене данных между системами, написанными на разных языках.

 

Шаг 1. Моделирование задачи

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

И так, представьте себе, что у нас есть игра, в которой у нас есть игрок(персона), естественно у него есть такие атрибуты как имя, возраст и друзья.

vectorpaint

Условия:

1. Мы не можем использовать базы данных, но при выходе мы должны сохранить все атрибуты нашего объекта.

2. В будущем, мы должны отправить данные на другой сервис, написанный на С++.

3. Возможность сохранить в другом виде(формате)

И так,  если смотреть на все просто, то можно:

«Простые» решения:

1. Сохранить в файл атрибуты, считывать их

2. Отправлять отдельно каждый атрибут сущности

3. Создать еще один парсер, со всеми вытекающими

Но, эти, на первый взгляд, «простые» решения содержат в себе подводные камни.

«Но» наших «простых» решений:

1. Нам нужно писать свой парсер чтобы разбирать файл с данными. Тем более, если объект содержит список объектов своего класса. В общем все не очень красиво.

2. В дальнейшем, при отправке на другой сервис, естественно, можно использовать атрибуты и отправлять их отдельно, но, это во первых не очень удобно, во вторых мы создаем свой «стандарт» который также нужно разбирать по своему.

3. Каждый раз все проблемы первого пункта.

В итоге, используя такой подход мы потратим много времени на написание парсеров, настройке, отладке, а также настройке 2 систем. Тем более, что если нам нужно поменять тип сохранения данных в какой-то другой формат(JSON)?

 

Шаг 2. Создание структуры проекта

Определимся с тем, что и как у нас будет строиться.

project_structure

entity — здесь размещаем наши сущности. По логике приложения у нас она одна — Person.

parser — здесь разместим классы, которые будут управлять парсингом нашего ентити.

Parser — интерфейс, который содержит в себе 2 метода для сохранения и для восстановления объекта.

Impl — пакет, внутри которого будут классы которые реализуют наш Parser. Таким образом мы можем легко менять парсеры в коде.

Помимо всего, у нас есть класс JaxbParserTest с помощью которого мы будем тестировать работу нашего парсера.

 

Шаг 3. Создание и аннотирование сущности

Создаем нашу сущность и аннотируем ее для JAXB. Если новая версия JDK выше 1.5, то подключать ее не нужно.

@XmlRootElement(name = "person")
@XmlType(propOrder = {"name","age","friends"})
public class Person {
    private String name;
    private int age;
    private List<Person> friends;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    @XmlElement
    public void setAge(int age) {
        this.age = age;
    }

    public List<Person> getFriends() {
        return friends;
    }

    @XmlElement(name = "friend")
    @XmlElementWrapper
    public void setFriends(List<Person> friends) {
        this.friends = friends;
    }

    @Override
    public String toString() {
        String res = "Person{" +
                "name='" + name + '\'' +
                ", age=" + age + ", friends{";
        if(friends != null){
            for(Person p : friends){
                res += p.toString();
            }
        }
        res += "}}";
        return res;
    }
}

@XmlRootElement(name = «person») — указывает на корневой тег. Имя указывать необязательно.

@XmlType(propOrder = {«name»,»age»,»friends»}) — указываем последовательность атрибутов в XML схеме.

@XmlElement — указывает на элемент который должен находиться в схеме. Можно не указывать, если элемент не должен менять свое имя.

@XmlElementWrapper — используется для коллекций, чтобы создать в схеме поверх них обертку. Таким образом, XML будет выглядеть более читабельно и понятно для человека.

 

Шаг 4. Создание парсера

В первую очередь, создаем наш интерфейс, от которого мы потом сможем двигаться в разные стороны, сделать приложение гибким.

public interface Parser {
    Object getObject(File file, Class c) throws JAXBException;
    void saveObject(File file, Object o) throws JAXBException;
}

Теперь создаем реализацию для нашего интерфейса.

public class JaxbParser implements Parser {
    @Override
    public Object getObject(File file, Class c) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(c);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        Object object = unmarshaller.unmarshal(file);

        return object;
    }

    @Override
    public void saveObject(File file, Object o) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(o.getClass());
        Marshaller marshaller = context.createMarshaller();
        marshaller.marshal(o,file);
    }
}

JAXBContext().newInstance() — создаем образец контекста и передаем Class объекта с которым будем работать

context.createUnmarshaller() — создаем анмаршаллер :-)

unmarshaller.unmarshall(file) — сохраняем данные схемы в объект. file — файл, с которого читаются данные.

context.createMarshaller() — создаем маршаллер. (запись объекта в файл в виде XML)

marshaller.marshal(o,file) — сохраняем объект o, в файл file.

 

Шаг 5. Пишем тест для проверки работы парсера

Теперь осталось проверить как работает наш код. Делаем сохранение и вывод восстановленных данных на экран.

public class JaxbParserTest {
    private Parser parser;
    private File file;

    @Before
    public void setUp() throws Exception {
        parser = new JaxbParser();
        file = new File("person.xml");
    }

    @Test
    public void testGetObject() throws Exception {
        Person person = (Person) parser.getObject(file, Person.class);
        System.out.println(person);
    }

    @Test
    public void testSaveObject() throws Exception {
        Person person = new Person();
        person.setName("Oleg");
        person.setAge(19);

        Person friend1 = new Person();
        Person friend2 = new Person();

        friend1.setName("Viktor");
        friend1.setAge(20);

        friend2.setName("Stepan");
        friend2.setAge(15);

        List<Person> friends = new ArrayList<Person>();
        friends.add(friend1);
        friends.add(friend2);

        person.setFriends(friends);

        parser.saveObject(file,person);
    }
}

result

 

Шаг 6. Аннотации

@XmlTransient — помечаются поля, которые не будут включены в маршалинг

@XmlSeeAlso — используется, когда в классе существует объект другого класса

@XmlEnum — для перечислений

@XmlEnumValue — значение для полей в перечислении

@XmlAccessorType — что именно будет сериализовано

@XmlElements — контейнер для нескольких @XmlElement

@XmlMimeType — mime-type для поля

Урок создан: 18 января 2014 | Просмотров: 35790 | Автор: Олег Криль | Правила перепечатки


Добавить комментарий

Добавить комментарий

Ваш e-mail не будет опубликован.

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

  • 13 апреля 2014 в 17:16

    Василий

    Спасибо Вам, Александр. Вы знаете, сколько ни практикуйся и не изучай технологии, всего знать не будешь. Часто хочется именно познакомиться с какой-нибудь технологией и понять, стоит ли углубляться. Приходится тратить кучу времени.
    Но не в ваших статьях. Здесь я, потратив 5 минут, узнал для чего эта технология, почему стоит её использовать, и нашел хороший понятный пример.
    Успехов Вам и Вашему проекту. Вы большой молодец!

    • 13 апреля 2014 в 17:35

      Александр Барчук

      Спасибо, будем стараться :) Но именно этот урок написал мой лучший друг и коллега по проекту Олег Криль, думаю данное спасибо ему :)

  • 21 июля 2014 в 23:42

    Кир

    Было бы вообще великолепно, если бы показали пример где Person наследуется например от People. И нам нужно например из родителя взять поле Пол.

  • 25 июля 2014 в 13:57

    Максим

    Не понятно каким образом определяется последовательность действий при выполнении тестов, можете пояснить?

    • 26 декабря 2015 в 18:53

      RJA

      Junit не гарантирует никакого порядка, кроме тех методов что упорядочились аннотациями. Но, мне по секрету сказали, что все происходит в обычном порядке, сверху вниз.

      • 29 февраля 2016 в 01:32

        Garry

        Это не рекомендуется, но если уже очень надо, поставьте для тестового класса аннотацию @FixMethodOrder(MethodSorters.NAME_ASCENDING)
        и тесты будут выполняться в алфавитном порядке названия методов