Работа с Json в 2019 году

Введение

В работе над своей небольшой библиотечкой jsp-openapi мне понадобилось сериализировать Java объекты в JSON. Я, не долго думая, подключил свою любимую библиотеку gson и пошёл дальше. Проект успешно проходил тест и собирался локально. Следующим моим шагом, как обычно, должна была стать сборка в travis-ci и подключение в sonarcloud.

Но что-то пошло не так

Вот ключевое место лога:

Caused by: java.lang.NoClassDefFoundError: java/sql/Time
	at com.google.gson.Gson.<init>(Gson.java:265)
	at com.google.gson.GsonBuilder.create(GsonBuilder.java:597)

Оказывается, gson зависит от пакета java.sql. При сборке jdk9+, библиотека должна создавать файл module-info.java, в котором необходимо описывать эту зависимость.

В принципе, это не страшно. Я даже уже начал идти этим путем: создал module-info.java, начал разбираться как собирать проект на openjdk11 и oraclejdk8. Но тут меня осенило. Я же разрабатываю библиотеку тэгов. Для JSP. В 2019 году. Какая вообще Java 11? В лучшем случае её будут использовать в Java 8. И ещё. Библиотека рендеринга html ну совсем не может зависеть от java.sql. Тем более, что я нигде не использую эту зависимость.

Немного взгрустнув, я вздохнул и пошёл сложной дорогой: выбор другой JSON библиотеки.

Выбор альтернатив

Самое быстрое гугление выдает 3 альтернативы:

Но прежде, чем выбирать какую-то одну, необходимо определиться с требованиями:

  • минимальное количество зависимостей, но без фанатизма. Код всё равно будет выполняться на сервере
  • возможность сериализировать POJO.
  • возможность отбрасывать null поля. Из-за того, что я не контролирую io.swagger.v3.oas.models.OpenAPI, то я не могу вставить в него аннотации и прочие служебные конструкции. Сериализация null полей должна быть сконфигурирована извне
  • возможность pretty print. Получившийся json будет рендериться с помощью тэгов <pre>, поэтому он должен выглядеть хорошо

json-java

Достаточно простой проект, мало кода, а значит быстро и ничего лишнего. Однако, сериализацию объектов через reflection не поддерживает и её придётся писать самому каждый раз. Не подходит.

Jackson

Нет, Вы только посмотрите на их readme. Если, с помощью Jackson нельзя собрать космический корабль, то я буду крайне удивлён.

После 15 минут вдумчивого анализа оказалось, что для минимальной работы требуется 3 библиотеки:

  • jackson-core
  • jackson-annotations
  • jackson-databind

Скрипя сердцем, отложил, как формально подходящую под требования.

jsonp

Чтобы начать работать с этой библиотекой, нужно добавить следующие зависимости:

<dependency>
    <groupId>javax.json</groupId>
    <artifactId>javax.json-api</artifactId>
    <version>1.1</version>
</dependency>

<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>javax.json</artifactId>
    <version>1.1</version>
</dependency>

Ох, только не glassfish. Почему выбор библиотеки может зависить от личных предпочтений? Тем не менее, прочитав случайную статью о сравнении сферических коней, решил не брать jsonp по более объективным причинам: низкая производительность.

Вывод

Jackson, конечно, уступает в удобстве работы gson. Но за неимением лучшего пришлось выбрать его:

JsonFactory jsonFactory = new JsonFactory();
jsonFactory.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false);
ObjectMapper mapper = new ObjectMapper(jsonFactory);
mapper.enable(SerializationFeature.INDENT_OUTPUT);
mapper.setSerializationInclusion(Include.NON_NULL);
mapper.writeValue(w, value.getProperties());

На этом я не остановился и выразил свою позицию разработчикам gson. Шансы, что эту задачу реализуют, крайне малы, так как это значит отказываться от обратной совместимости с предыдущими версиями. И на такое разработчики вряд ли пойдут.