Сравнение Json и YAML

в 6:35, , рубрики: java, json, yaml, десериализация, сериализация, форматы файлов

Пришел день, и конфигурационные файлы для нашего приложения стали настолько большими, что менеджеры намекнули что в JSON-конфигах получается подозрительно много фигурных и не фигурных скобочек, и им хотелось бы от них избавиться. Был дан тонкий намек, что неплохо бы приглядеться к YAML, ведь ходят слухи что он очень человекочитаемый. И скобочек никаких там нет. И списки красивые. Не внять старшим мы естественно не могли, вынуждены были изучать вопрос, искать разницу, плюсы и минусы обоих форматов. Очевидно, что такие сравнения затеваются лишь для того, чтобы подтвердить мнение руководителей или даже если не подтвердить, то они найдут почему они правы и почему стоит делать изменения :)

Сравнение Json и YAML

Уверен, что многие с данными форматами знакомы, но все же приведу краткое описание с википедии:

JSON (англ. JavaScript Object Notation) — текстовый формат обмена данными, основанный на JavaScript и обычно используемый именно с этим языком. Как и многие другие текстовые форматы, JSON легко читается людьми. Несмотря на происхождение от JavaScript (точнее, от подмножества языка стандарта ECMA-262 1999 года), формат считается языконезависимым и может использоваться практически с любым языком программирования. Для многих языков существует готовый код для создания и обработки данных в формате JSON.

YAML — человекочитаемый формат сериализации данных, концептуально близкий к языкам разметки, но ориентированный на удобство ввода-вывода типичных структур данных многих языков программирования. Название YAML представляет собой рекурсивный акроним YAML Ain't Markup Language («YAML — не язык разметки»). В названии отражена история развития: на ранних этапах язык назывался Yet Another Markup Language («Ещё один язык разметки») и даже рассматривался как конкурент XML, но позже был переименован с целью акцентировать внимание на данных, а не на разметке документов.

И так, что нам нужно:

  • сделать одинаковый сложный JSON и YAML
  • определить параметры по каким будем сравнивать
  • десиреализовать в JabaBeans около 30 раз
  • сравнить результат по скорости
  • сравнить читаемость файлов
  • сравнить удобство работы с форматом

Очевидно, что писать собственные парсеры мы не будем, поэтому для начала выберем для каждого формата по уже существующему парсеру.
Для json будем использовать gson (от google), а для yaml — snakeyaml (от не-знаю-кого).

Как видим все просто, нужно только создать достаточно сложную модель, которая будет имитировать сложность конфиг-файлов, и написать модуль который будет тестировать yaml и json парсеры. Приступим.
Нужна модель примерно такой сложности: 20 атрибутов разных типов + 5 коллекций по 5-10 элементов + 5 вложенных объектов по 5-10 элементов и 5 коллекций.
Этот этап всего сравнения смело можно назвать самым нудным и неинтересным. Были созданы классы, с незвучными именами типа Model, Emdedded1, и т.д. Но мы не гонимся за читаемостью кода (как минимум в этой части), поэтому так и оставим.

file.json

"embedded2": {
    "strel1": "el1",
    "strel2": "el2",
    "strel4": "el4",
    "strel5": "el5",
    "strel6": "el6",
    "strel7": "el7",
    "intel1": 1,
    "intel2": 2,
    "intel3": 3,
    "list1": [
      1,
      2,
      3,
      4,
      5
    ],
    "list2": [
      1,
      2,
      3,
      4,
      5,
      6,
      7
    ],
    "list3": [
      "1",
      "2",
      "3",
      "4"
    ],
    "list4": [
      "1",
      "2",
      "3",
      "4",
      "5",
      "6"
    ],
    "map1": {
      "3": 3,
      "2": 2,
      "1": 1
    },
    "map2": {
      "1": "1",
      "2": "2",
      "3": "3"
    }
  }

file.yml


embedded2:
  intel1: 1
  intel2: 2
  intel3: 3
  list1:
  - 1
  - 2
  - 3
  - 4
  - 5
  list2:
  - 1
  - 2
  - 3
  - 4
  - 5
  - 6
  - 7
  list3:
  - '1'
  - '2'
  - '3'
  - '4'
  list4:
  - '1'
  - '2'
  - '3'
  - '4'
  - '5'
  - '6'
  map1:
    '3': 3
    '2': 2
    '1': 1
  map2:
    1: '1'
    2: '2'
    3: '3'
  strel1: el1
  strel2: el2
  strel4: el4
  strel5: el5
  strel6: el6
  strel7: el7

Соглашусь, что человекочитаемость параметр достаточно субъективный. Но все таки, на мой взгяд, yaml немного более приятен взгляду и более интуитивно понятен.

Далее, для каждого парсера напишем небольшую обертку. С методами serialize() и deserialize().

yaml parser

public class BookYAMLParser implements Parser<Book> {
    String filename;

    public BookYAMLParser(String filename) {
        this.filename = filename;
    }

    @Override
    public void serialize(Book book) {
        try {
            DumperOptions options = new DumperOptions();
            options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
            Yaml yaml = new Yaml(options);
            FileWriter writer = new FileWriter(filename);
            yaml.dump(book, writer);
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Book deserialize() {
        try {
            InputStream input = new FileInputStream(new File(filename));
            Yaml yaml = new Yaml();
            Book data = (Book) yaml.load(input);
            input.close();

            return data;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (YamlException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            String message = "Exception in file " + filename + ", ";
            throw new Exception(message + e.getMessage());
        }
        return null;
    }
}
json parser

public class BookJSONParser implements Parser<Book> {

    String filename;
    public BookJSONParser(String filename) {
        this.filename = filename;
    }

    @Override
    public void serialize(Book book) {
        Gson gson = new GsonBuilder().setPrettyPrinting().create();;

        try {
            FileWriter writer = new FileWriter(filename);
            String json = gson.toJson(book);
            writer.write(json);
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Book deserialize() {
        Gson gson = new Gson();

        try {
            BufferedReader br = new BufferedReader(
                    new FileReader(filename));

            JsonReader jsonReader = new JsonReader(br);
            Book book = gson.fromJson(jsonReader, Book.class);
            return book;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

Как мы видим, оба формата имеют поддержку в java. Но для json выбор намного шире, это бесспорно.
Парсеры гоотовы, теперь рассмотрим реализацию сравнения. Тут тоже все предельно просто и очевидно. Есть простой метод, который 30 раз десериализует объекты из файла. Если кому интересно — код под спойлером.

json parser

public static void main(String[] args) {
      String jsonFilename = "file.json";

      String yamlFilename = "file.yml";
      BookJSONParser jsonParser = new BookJSONParser(jsonFilename);
      jsonParser.serialize(new Book(new Author("name", "123-123-123"), 123, "dfsas"));

      BookYAMLParser yamlParser = new BookYAMLParser(yamlFilename);
      yamlParser.serialize(new Book(new Author("name", "123-123-123"), 123, "dfsas"));

      //json deserialization
      StopWatch stopWatch = new StopWatch();
      stopWatch.start();
      for (int i = 0; i < LOOPS; i++) {
          Book e = jsonParser.deserialize();
      }
      stopWatch.stop();
      System.out.println("json worked: " + stopWatch.getTime());

      stopWatch.reset();

      //yaml deserialization
      stopWatch.start();
      for (int i = 0; i < LOOPS; i++) {
          Book e;
          e = yamlParser.deserialize();
      }
      stopWatch.stop();
      System.out.println("yaml worked: " + stopWatch.getTime());

  }

В реультате получаем следующий результат:

json worked: 278
yaml worked: 669

Сравнение Json и YAML

Как видно, json файлы парсятся примерно в три раза быстрее. Но абсолютная разница не является критичной, в наших масштабах. Поэтому это не сильный плюс в пользу json.
Это происходит потому что json парсится «на лету», то есть считывается посимвольно и сразу сохраняется в объект. Получается объект формируется за один проход по файлу. На самом деле я не знаю как работает именно этот парсер, но в общем схема такая.
А yaml, в свою очередь, более размеренный. Этап обработки данных делится на 3 этапа. Сначала строится дерево объектов. Потом оно еще каким-то образом преобразовывается. И только после этого этапа конвертируется в нужные структуры данных.

Небольшая сравнительная таблица ("+" — явное превосходство, "-" — явное отставание, "+-" — нет явного преимущества):

Json YAML
скорость работы + -
человекочитаемость - +
поддержка в java +- +-

Как это можно подытожить?
Тут все очевидно, если вам важна скорость — тогда json, если человекочитаемость — yaml. Нужно просто решить, что важнее. Для нас оказалось — второе.
На самом деле, тут можно привести еще множество различных доводов в пользу каждого из форматов, но я считаю, что самые важные все таки эти два пункта.

Далее, при работе с yaml мне пришлось столкнусть с не очень красивой обработкой исключений, особенно при синтаксических ошибках. Также, пришлось протестировать различные yaml библиотеки. Еще, в завершение нужно было написать какую-нибудь валидацию. Были опробованы валидацию при помощи схем (там приходилось выхывать руби гемы), и bean-валидация на основе jsr-303. Если вас интересует какая-либо из этих тем — буду рад ответить на вопросы.
Спасибо за внимание:)

P.S.
Уже под конец написания статьи наткнулся на следующее сравнение yaml и json:
www.csc.kth.se/utbildning/kth/kurser/DD143X/dkand11/Group2Mads/victor.hallberg.malin.eriksson.report.pdf

На самом деле, появилось ощущения, что я проделал уже сделанную работу, причем сделанную более основательно и подробно, но ведь главное — это полученный опыт :)

P.P.S.
Описание работы парсеров взял оттуда. Мои извинения если перевод недостаточно точен и понятен.

Автор: motiamotia

Источник

Поделиться

* - обязательные к заполнению поля