Сравнение 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
  • определить параметры по каким будем сравнивать
  • десериализовать в Java объекты около 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 раз десериализует объекты из файла. Если кому интересно — код под спойлером.

testing code
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 файлы парсятся примерно в три раза быстрее. Но абсолютная разница не является критичной, в наших масштабах. Поэтому это не сильный плюс в пользу 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.
Описание работы парсеров взял оттуда. Мои извинения если перевод недостаточно точен и понятен.
Share post

Comments 22

    0
    А вы точно в начале примеры файлов для json и yml друг с другом не перепутали? Первый раз вижу такой, якобы, json.
      0
      Да, действительно, я перепутал. Спасибо, исправил.
        0
        соответственно многие дальнейшие выводы о json, yml, скорее всего, перепутаны друг с другом?
          0
          Нет, тут все в порядке. Я перепутал именно когда вставлял код или названия спойлеров. Но, спасибо, заставили еще раз все проверить.
            +4
            В этом случае, очень смущает вывод о том, что json — нечеловекочитаемый.
              –1
              Было сказано только то, что, на мой взгляд, yaml имеет преимущество перед json по этому параметру.
      +1
      После редактирования в одном проекте простыни composer.json на очень много строк и километров yaml-конфигов для себя понял, что JSON нельзя назвать форматом, читаемым/редактируемым человеком. По сравнению с YAML, ведь все познается в сравнении. Хотя до JSON был XML, который читался еще хуже.
        0
        Я выводу про нечитаемость json удивлен. Поясните, почему 1, 2 и 3 относится к map2, а street1: el1 и остальные нет? Если ответ будет «пробел», то при сложной вложенности читаемость yaml будет просто ужасна, тогда как json явно позволяет разделять эти вещи.
        map2:
            1: '1'
            2: '2'
            3: '3'
          strel1: el1
          strel2: el2
          strel4: el4
          strel5: el5
          strel6: el6
          strel7: el7
        
          0
          Но разве это валидный yml?
            0
            С форматом не работал. Предположу, что вы придрались к 1 строчке — добавьте в ее начало 2 пробела.
            –2
            Я только сказал, что на мой взгляд, yaml более приятен глазу. Вот ссылка на yaml файл с большей вложенностью:
            bitbucket.org/mnazaruk/jsonvsyaml/src/2472c16746b2aabda8b7002750b1d103f8af15bb/yamlfiles/parsing/file.yml?at=master

            Не сказал бы, что все выглядит ужасно. Отлично читается.
            Изначально сказано, что в json смущает лишь наличие множества скобок. И если вдруг вам yaml где-то кажется плохо читаемым ввиду отсутствия скобок — из всегда можно добавить и сделать как вы бы делали в json. Файл с json структурой является валидным yaml файлом.
              0
              В вашей таблице написано «человекочитаемость». Я искренне считаю, что скобки намного понятнее, чем пробелы.
              Замечу, что в примере по ссылке не большая вложенность, а большее количество различных записей.
                –1
                Да, так и написано. Но там еще и написано, что значат плюсы и минусы в ней. И минус — это всего лишь отставание, а не нечитаемость.
                Насчет скобок. Можно ли провести такое сравнение: вы считаете что языки ruby и python намного менее понятны и читаемы чем, например java и c#?
                  +1
                  Вы противоречите сами себе. В тексте написано:
                  "+" — явное превосходство, "-" — явное отставание
                  

                  Пробел явно менее читаем, чем квадратная скобка, ибо в месте, где имеется иерархия в 10 уровней, в пробелах вы закопаетесь. Также json невероятно ужимаем, ибо в том же yaml из вашей ссылки на pdf файл указан пример:
                  # Valid JSON but invalid YAML due to missing whitespace
                  {"name":"Malin", "school": "KTH",year: 2008}
                  Я всего лишь не поставил пробел перед «year», а данный пример уже вызовет crash при парсинге.

                  При больших объемах данных, json очень ужимаем, в yaml же при глубоких уровнях вложенности (например в 10 уровней) дочерние элементы, а именно каждый из них обзаведутся множеством ненужных пробелов, тогда как json может не хранить даже \r\n и быть всего длиной в 1 строку.

                  Также вы сравниваете несравнимое. И в ruby, и в python хороший синтаксис и в них есть и def и return и т.д. Но в отличии от того же java, python скриптовый и создан для других целей, хотя и там инструкции не зависят от количества пробелов и приложение не будет вести себя иначе, если я вдруг поставлю лишний.
                    0
                    Если для вас удобнее читается скобка, то это не значит что и для меня тоже.

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

                    Но вам я хотел только сказать: не очевидно, что отсутствие скобок делает код намного более нечитаемым. Очевидно, что в некоторых случаях код получается даже лучше и приятней для глаз. В моем случае — мне подошел вариант с yaml. И это никак не умаляет достоинств json. Ведь мне не критичны размер файла, или строгость синтаксиса yaml. Самым важным была — читаемость (и немного время работы), которая является достаточно субъективным параметром.
            +5
            Холиварная тема. Не понятно, почему у yaml читабельность лучше? Можно же всегда красиво развернуть json, будет тоже самое что yaml, но со скобками.
              –2
              почему у yaml читабельность лучше

              но со скобками

              Вы сами ответили на свой вопрос :).
            • UFO just landed and posted this here
                +2
                C JSON все ОК, кроме одного — закомментить нельзя. А в YAML можно.
                  0
                  статья ниочем
                    0
                    Очень грамотно и содержательно.
                      0
                      не обижайтесь, но очеееень субъективные и необоснованные выводы, проще было просто оформить ссылкой на документ, который в конце приложен, без самой статьи.

                  Only users with full accounts can post comments. Log in, please.