- PVSM.RU - https://www.pvsm.ru -

Взаимные превращения JSON, YAML, XML

JSON, YAML сейчас популярны, а XML технологии считаются пережитком прошлого.

Взаимные превращения JSON, YAML, XML - 1

Попробуем использовать «ретро технологии» для работы с данными в формате JSON и YAML. И порассуждаем о причинах применять их в наши дни.

Есть задача — логику трансформации данных вынести в конфигурацию приложения, желательно в декларативном стиле и унифицировано для разных форматов. Данные могут быть в различных текстовых форматах сериализации json, yaml, xml, java properties, ini file. Но при этом Data Lake слишком тяжелая артиллерия для этого. Помещать данные в документоориентированную или объектно-реляционную базу и пытаться выполнять запросы по загруженным туда данным тоже over engineering для первого этапа ETL трансформации.

JsonPath [1] повторяет подмножество XPath, но только к JSON формату. И написать декларативный запрос без программирования не выйдет — нет аналога XQuery. Как вариант — можно было бы использовать какую-либо embedded database в jvm с ее декларативным языком запросом, но это тема для отдельной публикации и исходная модель данных в json, yaml не реляционная.

Подход к запросам данных из JSON/YAML

XQuery можно выполнять над данными в Document Object Model. Как бы преобразовать данные из JSON/YAML в DOM объект… Можно воспользоваться camel-xmljson [2] или json2xml [3]. В этих библиотеках источник данных только json. Поэтому помчим на своем dom-transformation [4] велосипеде. Эта библиотека умеет принимать на вход Map<String, Object> и превращать его в org.w3c.dom.Node, а так же есть обратное преобразование.

Осталось научиться превращать JSON и YAML в Map<String, Object>. Например, это можно сделать с помощью класса com.fasterxml.jackson.databind.ObjectMapper из jackson [5].

Превращаем JSON в Map:

ObjectMapper mapper = new ObjectMapper();
Map<String, Object> objectTree = mapper.readValue(yaml, new TypeReference<Map<String, Object>>() {});

Превращаем YAML в Map:

ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
Map<String, Object> objectTree = mapper.readValue(yaml, new TypeReference<Map<String, Object>>() {});

Превращаем Map в Document Object Model, подключив к проекту библиотеку [6]:


DomTransformer toDom = new DomTransformer(new TypeAutoDetect()).transform(objectTree.size() == 1 ? objectTree : Collections.singletonMap("root", objectTree));
Node document = toDom.translate(objectTree);

Можно использовать любую реализацию XQuery для выполнения запросов. Мне нравится basex [7] как до сих пор развивающийся open source проект. Подключаем к проекту зависимость org.basex:basex:jar:9.0 [8] и выполняем декларативный запрос:


String yaml = IOUtils.toString(TranslateTest.class.getResource("/pipeline.yml").toURI(), StandardCharsets.UTF_8);
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
Map<String, Object> objectGraph = mapper.readValue(yaml, new TypeReference<Map<String, Object>>() {});

Node document = new DomTransformer(new TypeAutoDetect()).transform(
                        objectGraph.size() == 1 ? objectGraph : Collections.singletonMap("root", objectGraph));

try(QueryProcessor proc = new QueryProcessor("declare variable $extDataset external; " +
        " $extDataset//*[text()='git-repo']", new Context())) {
    proc.bind("extDataset", document);
    Value queryResult = proc.value(); // execute the query
    queryResult.iter().forEach(System.out::println);
}

Результаты работы для данных из pipeline.yml [9]

Взаимные превращения JSON, YAML, XML - 2

Если же нужно преобразовать DOM/XML в JSON/YAML с помощью jackson, то transform(Node currentNode) [10] может помочь в этом.

Выводы

С помощью XQuery можно выполнять запросы не только к XML данным. С чем этот язык запросов до сих пор успешно справляется и этот «старичок» еще поживет в java проектах по трансформации данных даже в форматах JSON и YAML.

Конечно, слабоструктурированные данные это не только JSON, YAML и XML. И ставить точку в обработке всего на свете еще рано…

Взаимные превращения JSON, YAML, XML - 3

Надеюсь, что подход из публикации поможет вам выполнять в приложении декларативные запросы по разнородным данным. Или же вы сталкивались с подобной задачей в JVM и у вас есть идеи лучше, делитесь в комментариях!

Автор: igor_suhorukov

Источник [11]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/java/277013

Ссылки в тексте:

[1] JsonPath: http://goessner.net/articles/JsonPath/

[2] camel-xmljson: http://camel.apache.org/xmljson.html

[3] json2xml: https://github.com/lukas-krecan/json2xml

[4] dom-transformation: https://github.com/igor-suhorukov/dom-transformation

[5] jackson: https://github.com/FasterXML/jackson

[6] библиотеку: https://mvnrepository.com/artifact/com.github.igor-suhorukov/dom-transformation/1.2

[7] basex: http://basex.org

[8] org.basex:basex:jar:9.0: https://mvnrepository.com/artifact/org.basex/basex/9.0

[9] pipeline.yml: https://github.com/spring-projects/spring-boot/blob/a938c372eaa7bc8d8d36ee448aeca5054b364226/ci/pipeline.yml

[10] transform(Node currentNode): https://github.com/igor-suhorukov/dom-transformation/blob/master/src/main/java/com/github/igorsuhorukov/dom/transform/DomTransformer.java#L143

[11] Источник: https://habrahabr.ru/post/352810/?utm_campaign=352810