Обработка JSON в Java EE 7

Обработка JSON в Java EE 7

1. обзор

Эта статья покажет вам, как обрабатывать JSON, используя только ядро ​​Java EE, без использования сторонних зависимостей, таких как Джерси или Джексон. Практически все, что мы будем использовать, предоставляется пакетомjavax.json.

2. Запись объекта в JSONString

Преобразовать объект Java в JSONString очень просто. Предположим, у нас есть простой классPerson:

public class Person {
    private String firstName;
    private String lastName;
    private Date birthdate;

    // getters and setters
}

Чтобы преобразовать экземпляр этого класса в JSONString, сначала нам нужно создать экземплярJsonObjectBuilder и добавить пары свойство / значение с помощью методаadd():

JsonObjectBuilder objectBuilder = Json.createObjectBuilder()
  .add("firstName", person.getFirstName())
  .add("lastName", person.getLastName())
  .add("birthdate", new SimpleDateFormat("DD/MM/YYYY")
  .format(person.getBirthdate()));

Обратите внимание, что методadd() имеет несколько перегруженных версий. Он может получить большинство примитивных типов (а также объекты в штучной упаковке) в качестве второго параметра.

После того, как мы закончили установку свойств, нам просто нужно записать объект вString:

JsonObject jsonObject = objectBuilder.build();

String jsonString;
try(Writer writer = new StringWriter()) {
    Json.createWriter(writer).write(jsonObject);
    jsonString = writer.toString();
}

Вот и все! СгенерированныйString будет выглядеть так:

{"firstName":"Michael","lastName":"Scott","birthdate":"06/15/1978"}

2.1. ИспользованиеJsonArrayBuilder для построения массивов

Теперь, чтобы немного усложнить наш пример, предположим, что классPerson был изменен для добавления нового свойства с именемemails, которое будет содержать список адресов электронной почты:

public class Person {
    private String firstName;
    private String lastName;
    private Date birthdate;
    private List emails;

    // getters and setters

}

Чтобы добавить все значения из этого списка вJsonObjectBuilder, нам понадобится помощьJsonArrayBuilder:

JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();

for(String email : person.getEmails()) {
    arrayBuilder.add(email);
}

objectBuilder.add("emails", arrayBuilder);

Обратите внимание, что мы используем еще одну перегруженную версию методаadd(), которая принимает объектJsonArrayBuilder в качестве второго параметра.

Итак, давайте посмотрим на сгенерированную строку для объектаPerson с двумя адресами электронной почты:

{"firstName":"Michael","lastName":"Scott","birthdate":"06/15/1978",
 "emails":["[email protected]","[email protected]"]}

2.2. Форматирование вывода с помощьюPRETTY_PRINTING

Итак, мы успешно преобразовали объект Java в действительный JSONString. Теперь, прежде чем перейти к следующему разделу, давайте добавим простое форматирование, чтобы сделать вывод более «JSON-подобным» и более удобным для чтения.

В предыдущих примерах мы создалиJsonWriter, используя простой статический методJson.createWriter(). Чтобы получить больший контроль над сгенерированнымиString, мы будем использовать возможность Java 7JsonWriterFactory для создания модуля записи с определенной конфигурацией.

Map config = new HashMap<>();

config.put(JsonGenerator.PRETTY_PRINTING, true);

JsonWriterFactory writerFactory = Json.createWriterFactory(config);

String jsonString;

try(Writer writer = new StringWriter()) {
    writerFactory.createWriter(writer).write(jsonObject);
    jsonString = writer.toString();
}

Код может показаться немного многословным, но на самом деле ничего не делает.

Сначала он создает экземплярJsonWriterFactory, передавая карту конфигурации своему конструктору. Карта содержит только одну запись, которая устанавливает значение true для свойства PRETTY_PRINTING. Затем мы используем этот экземпляр фабрики для создания модуля записи вместо использованияJson.createWriter().

Новый вывод будет содержать характерные разрывы строк и табуляцию, характеризующую JSONString:

{
    "firstName":"Michael",
    "lastName":"Scott",
    "birthdate":"06/15/1978",
    "emails":[
        "[email protected]",
        "[email protected]"
    ]
}

3. Создание JavaObject изString

Теперь давайте проделаем обратную операцию: преобразуем JSONString в объект Java.

Основная часть процесса преобразования вращается вокругJsonObject. Чтобы создать экземпляр этого класса, используйте статический методJson.createReader(), за которым следуетreadObject():

JsonReader reader = Json.createReader(new StringReader(jsonString));

JsonObject jsonObject = reader.readObject();

МетодcreateReader() принимает в качестве параметраInputStream. В этом примере мы используемStringReader,, поскольку наш JSON содержится в объектеString, но этот же метод можно использовать для чтения содержимого из файла, например, используяFileInputStreamс.

Имея под рукой экземплярJsonObject, мы можем прочитать свойства с помощью методаgetString() и присвоить полученные значения вновь созданному экземпляру нашего классаPerson:

Person person = new Person();

person.setFirstName(jsonObject.getString("firstName"));
person.setLastName(jsonObject.getString("lastName"));
person.setBirthdate(dateFormat.parse(jsonObject.getString("birthdate")));

3.1. ИспользованиеJsonArray для получения значенийList

Нам понадобится использовать специальный классJsonArray для извлечения значений списка изJsonObject:

JsonArray emailsJson = jsonObject.getJsonArray("emails");

List emails = new ArrayList<>();

for (JsonString j : emailsJson.getValuesAs(JsonString.class)) {
    emails.add(j.getString());
}

person.setEmails(emails);

Это оно! Мы создали полный экземплярPerson из JsonString.

4. Запрос значений

Теперь предположим, что нас интересует очень конкретный фрагмент данных, который находится внутри JSONString.

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

{
    "ownerName": "Robert",
    "pets": [{
        "name": "Kitty",
        "type": "cat"
    }, {
        "name": "Rex",
        "type": "dog"
    }, {
        "name": "Jake",
        "type": "dog"
    }]
}

Преобразование всего текста в объект Java только для получения единственного значения было бы не очень эффективным. Итак, давайте проверим пару стратегий для запроса JSONStrings без необходимости проходить через все испытания конверсии.

4.1. Запросы с использованием API объектной модели

Запросить значение свойства с известным местоположением в структуре JSON очень просто. Мы можем использовать экземплярJsonObject, того же класса, что и в предыдущих примерах:

JsonReader reader = Json.createReader(new StringReader(jsonString));

JsonObject jsonObject = reader.readObject();

String searchResult = jsonObject
  .getJsonArray("pets")
  .getJsonObject(2)
  .getString("name");

Уловка здесь в том, чтобы перемещаться по свойствамjsonObject, используя правильную последовательность методовget*().

В этом примере мы сначала получаем ссылку на список «домашних животных», используя методgetJsonArray(),_ which returns a list with 3 records. Then, we use _getJsonObject(), который принимает индекс в качестве параметра, возвращая другойJsonObject, представляющий третий элемент в список. Наконец, мы используемgetString(), чтобы получить строковое значение, которое мы ищем.

4.2. Запросы с использованием Streaming API

Другой способ выполнять точные запросы к JSONString - использовать Streaming API, в которомJsonParser является основным классом.

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

JsonParser jsonParser = Json.createParser(new StringReader(jsonString));

int count = 0;
String result = null;

while(jsonParser.hasNext()) {
    Event e = jsonParser.next();

    if (e == Event.KEY_NAME) {
        if(jsonParser.getString().equals("name")) {
            jsonParser.next();

            if(++count == 3) {
                result = jsonParser.getString();
                break;
            }
        }
    }
}

Этот пример дает тот же результат, что и предыдущий. Он возвращаетname от третьего питомца в спискеpets.

После того, какJsonParser создан с использованиемJson.createParser(), нам нужно использовать итератор (отсюда и характер «прямого доступа»JsonParser) для навигации по токенам JSON, пока мы не дойдем до свойства (или свойства) мы ищем.

Каждый раз, когда мы проходим через итератор, мы переходим к следующему токену данных JSON. Поэтому мы должны быть осторожны, чтобы проверить, имеет ли текущий токен ожидаемый тип. Это делается путем проверкиEvent, возвращаемого вызовомnext().

Существует много разных типов токенов. В этом примере нас интересуют типыKEY_NAME, которые представляют имя свойства (например, «OwnerName», «домашние животные», «имя», «тип»). После того, как мы в третий раз прошли через токенKEY_NAME со значением «name», мы знаем, что следующий токен будет содержать строковое значение, представляющее имя третьего питомца из списка.

Это определенно сложнее, чем использование API Object Model, особенно для более сложных структур JSON. Выбор между тем или иным, как всегда, зависит от конкретного сценария, с которым вы будете иметь дело.

5. Заключение

Мы рассмотрели многое из API обработки JSON Java EE на нескольких простых примерах. Чтобы узнать о других интересных вещах об обработке JSON, проверьте нашseries of Jackson articles.

Проверьте исходный код классов, используемых в этой статье, а также некоторых модульных тестов в нашемGitHub repository.