1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/eric_ds-jfire-codejson

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
ReadMe.md

Jfire-codejson — это фреймворк с высочайшей производительностью для работы с JSON.

Преимущества фреймворка:

  • Высокая производительность. Codejson является самым мощным на данный момент фреймфорком для работы с JSON, его производительность в 2,1 раза выше, чем у fastjson, в 1,8 раза выше, чем у jackson2 и в 6,7 раза выше, чем у gson. На графике ниже представлены результаты сравнительного анализа производительности. Изображение
  • Настраиваемый вывод. Помимо стандартных функций других фреймворков для работы с JSON codejson предоставляет возможность настраивать вывод данных. Можно настроить вывод так, чтобы один и тот же объект имел совершенно разные результаты. Например, можно настроить вывод числа с плавающей точкой с двумя или тремя знаками после запятой, а также можно игнорировать определённые атрибуты объекта или выводить их.
  • Простота API. Для сериализации достаточно использовать статический метод JsonTool.write(entity). Для десериализации — JsonTool.read(User.class, str).
  • Поддержка циклических ссылок. Codejson поддерживает работу с циклическими ссылками. Он определяет их на основе принципа поиска в глубину. Кроме того, codejson позволяет настраивать результат вывода при возникновении циклических ссылок.

Быстрый старт

Предположим, что есть несколько классов:

public class Person {
    @JsonIgnore // поддержка аннотации для игнорирования атрибутов
    private String name;
    @@JsonRename("a") // поддержка аннотаций для переименования атрибутов
    private int age;
    private boolean boy;
}
package link.jfire.test;
public class Home {
    privaet String name;
    private Person host;
    private float height;
    private float weidth;
}
public static void main(String args[]) {
    Home home = new Home();
    home.setPerson(new Person());
    // преобразование home в строку JSON
    String json = JsonTool.write(home);
    // преобразование строки JSON в объект JSON
    JsonObject jsonObject = (JsonObject) jsonTool.fromString(json);
    // преобразование строки JSON в Java-объект
    Home result = JsonTool.read(Home.class, json);

    WriteStrategy strategy = new WriteStrategy();
    // определение стратегии вывода, которая заменяет имя атрибута на "hello" при выводе
    strategy.addRenameField("link.jfire.test.Home.name", "hello");
    json = strategy.write(home);

    strategy = new WriteStrategy();
    // определение стратегии вывода, ограничивающей количество знаков после запятой до одного при выводе чисел с плавающей запятой
    // WriteAdapter — это оболочка, которая просто переопределяет все методы интерфейса JsonWriter. В зависимости от типа свойства необходимо выбрать соответствующий метод стратегии для переопределения и изменения.
    strategy.addWriter(float.class, new WriterAdapter() {
        @override
        public void write(float target, StringCache cache, Object entity) {
            DecimalFormat format = new DecimalFormat("##.00");
            cache.append(format.format(target));
        }
    });
    json = strategy.write(home);
}

Код и пояснения

Код доступен по адресу: адрес.

Maven-зависимости:

<dependency>
    <groupId>link.jfire</groupId>
    <artifactId>codejson</artifactId>
    <version>1.1</version>
</dependency>

API:

  • Сериализация. Для обычной сериализации используйте метод JsonTool.write(entity), который преобразует объект в строку JSON.
  • Десериализация.
    • Преобразование в JSON. Используйте метод JsonTool.fromString(str), чтобы преобразовать строку JSON в объект JSON (может быть JsonObject или JsonArray).
    • Преобразование в POJO. Используйте метод JsonTool.read(User.class,str), чтобы преобразовать строку JSON в объект класса User.
  • Игнорирование и переименование. Игнорирование и переименование атрибутов осуществляется с помощью аннотаций @JsonIgnore и @JsonRename. При сериализации аннотацию можно разместить на методе get, а при десериализации — на методе set. Если аннотация размещена на атрибуте, она будет применяться как при сериализации, так и при десериализации. Аннотации игнорирования не действуют в стратегиях, если стратегия не включает игнорирование этого атрибута. Чтобы принудительно игнорировать атрибут, используйте @JsonIgnore(force=true).
  • Стратегии. Стратегии позволяют настраивать вывод объектов. Они могут использоваться для игнорирования определённых атрибутов или для изменения имён атрибутов.
    1. Создайте объект стратегии, например, WriteStrategy strategy = new WriteStrategy().
    2. Добавьте конкретные стратегии, такие как игнорирование атрибута с помощью strategy.addIgnoreField("link.jfire.test.User.age"), где формат параметра — класс.атрибут.
    3. Используйте стратегию, например, для вывода с помощью метода strategy.write(entity). Стратегия strategy действует как контекст, содержащий всю информацию. Объект стратегии создаётся один раз и может использоваться повсеместно. Этот объект является потокобезопасным.

Конкретные способы добавления стратегий:

  • Добавление стратегии игнорирования. Используйте код strategy.addIgnore("link.jfire.test.User.age") с параметром класс.атрибут.
  • Добавление стратегии переименования. Используйте код strategy.addRenameField("link.jfire.test.User.age", "AGE"), где первый параметр указывает на атрибут, а второй — новое имя.
  • Добавление стратегии для определённого типа. Например, для форматирования всех значений типа double с точностью до двух знаков после запятой используйте код:
WriteStrategy strategy = new WriteStrategy();
// Форматирование всех значений double с точностью до 2 знаков после запятой
strategy.addTypeStrategy(double.class, new WriterAdapter() {
    public void write(double field, StringCache cache,Object entity)
    {
        DecimalFormat format = new DecimalFormat("##.00");
        cache.append(format.format(field));
    }
});
// a — тип float, b — double. Видно, что значение double ограничено двумя знаками после запятой.
String except = "{\"a\":2.2365,\"b\":15.69,\"percent\":\"88.81%\"}";
``` ```
assertEquals(except, strategy.write(new BaseData()));

Добавление стратегии для определённого свойства

WriteStrategy strategy = new WriteStrategy();
    // Первый параметр — это позиция свойства в виде полного имени класса.имя свойства
    // Второй параметр — анонимный класс, который выражает требования пользовательской стратегии
strategy.addFieldStrategy("link.jfire.test.User.address", new WriterAdapter() {
        public void write(Object field, StringCache cache, Object entity) {
            User user = (User) field;
            // Выводим только первые три символа адреса
            cache.append(user.getAddress().subString(0, 3)).append("...");
        }
    });
    // Вывод: {"name":"test","address":"仓山区..."}
    // Видно, что вывод определённого свойства был изменён
    strategy.write(new User());

Циклические ссылки

В некоторых случаях сериализации могут возникать циклические ссылки на объекты. В таких случаях необходимо включить функцию циклических ссылок в стратегии, иначе объекты не смогут быть правильно сериализованы. Включить функцию циклических ссылок очень просто, достаточно одной строки кода WriteStrateg.setUseTracker(true); При наличии циклических ссылок объект больше не будет сериализован, вместо этого будет выведено его местоположение в системе объектов. Вывод будет выглядеть как {"$ref":"$.data.k"}, где $ обозначает корневой объект. Помимо возможности вывода пути объекта с циклической ссылкой, можно также настроить вывод для циклических ссылок. Например, если определённый класс имеет циклическую ссылку, можно вывести дополнительную информацию. Для получения дополнительной информации см. следующий код

Room room = new Room();
    room.setLength(100);
    Guy guy = new Guy();
    guy.setName("sadasd");
    guy.setRoom(room);
    room.setGuy(guy);
    WriteStrategy strategy = new WriteStrategy();
    strategy.setUseTracker(true);
    strategy.addTrackerType(Guy.class, new WriterAdapter() {
        @Override
        public void write(Object field, StringCache cache, Object entity, Tracker tracker) {
            Guy guy = (Guy) field;
            String path = tracker.getPath(field);
            cache.append("{\"$ref\":").append(path).append(",\"Я хочу вывести всё что угодно\":\"").append(guy.getName()).append("\"}");
            System.err.println(tracker.getPath(field));
        }
    });
    System.out.println(strategy.write(guy));
//Вывод: {"name":"sadasd","room":{"guy":{"$ref":$,"Я хочу вывести всё что угодно":"sadasd"},"length":100}}
## Анализ производительности
Jfire-codejson обладает такой высокой производительностью благодаря использованию уникального алгоритма.
**Сериализация**
Традиционные сериализаторы или относительно продвинутые сериализаторы в основном используют анализ содержимого объекта, а затем используют отражение для вызова методов или извлечения значений свойств для сериализации объекта. Этот подход приводит к узким местам в производительности из-за отражения. Jfire-codejson использует уникальный подход, динамически компилируя выходной объект для каждого сериализуемого объекта, и все значения свойств получаются через вызовы метода get для объекта. Кроме того, при объединении ключей JSON во время компиляции кода заранее известны и записаны, что уменьшает шаг получения имён свойств объекта.** Это делает сериализацию Jfire-codejson близкой к теоретическому пределу** (теоретический подход написания кода для каждого объекта).
**Десериализация**
Процесс десериализации начинается с анализа строки JSON. На этом этапе фреймворк использует метод однократного последовательного чтения символов. Основная идея заключается в последовательном чтении каждого символа и генерации объектов JSONObject или JSONArray при обнаружении определённых символов, таких как `{`, `}`, `:`, `[`, `]` и т. д. Используются два стека: один для хранения ключей JSON, а другой для текущего обрабатываемого объекта JSON (JSONObject или JSONArray). Этот подход обеспечивает последовательную обработку во время анализа.** Скорость анализа очень высокая, она может достигать нескольких раз скорости fastjson**.
Для десериализации JSON-объекта в POJO используется тот же принцип, что и для сериализации. Создаётся динамический класс установки, который генерирует код, подобный `if(jsonObject.containsKey("name")){entity.setName(jsonObject.getString("name"))}` для каждого метода set. Поскольку это динамическая компиляция, заранее известно, какие проверки необходимы, и после завершения проверки выполняются собственные операции установки, что экономит время на проверку и вызов отражения, поэтому производительность десериализации также очень хорошая.** В несколько раз быстрее, чем fastjson**.

### Сериализация
Если нужно сериализовать объект, самый быстрый способ  написать специальный код для этого объекта. Код получает значения свойств объекта через вызовы методов get. См. следующий пример кода
```java
public class User
{
    private String name;
    private int age;
}
//Пример кода сериализации для вышеуказанного класса
public static void main(String args[])
{
    User user = new User();
    StringBuilder str = new StringBuilder();
    str.append("{");
    str.append("\"name\":");//Нет необходимости вставлять значение переменной, использование фиксированного значения экономит время и способствует оптимизации JVM
    str.append("\"").append(user.getName()).append("\",");
    str.append("\"age\":").append(user.getAge());
    str.append("}");
}

В приведённом выше примере кода нет отражения или анализа. Он представляет собой конкретный код сериализации, предназначенный для данного класса. Если наш фреймворк также может имитировать этот подход, мы можем достичь максимальной скорости сериализации и приблизиться к теоретическому верхнему пределу. Чтобы достичь этого эффекта, используется динамическая компиляция кода.

  1. Сначала получите объект Class целевого класса. Получите все методы get (или is, методы должны соответствовать спецификации javabean) из этого объекта Class.
  2. Скомпилируйте динамический код, сначала создайте StringBuilder для хранения строки JSON. Также необходим экземпляр объекта для компиляции динамического кода.
  3. Для каждого метода, полученного на шаге 1, извлеките имя свойства в соответствии со спецификацией javabean. Создайте код, похожий на builder.append("name").append(":\"").append(entity.getName()).append("\",)
  4. После завершения обработки всех методов get скомпилируйте этот код в отдельный класс сериализации. Создайте карту для сопоставления объектов с классами сериализации. Если во время процесса обработки на шаге 3 обнаруживается свойство не базового типа, скомпилируйте код, похожий наbuilder.append("anotherObject").append(":");WriteContext.write(entity.getAnotherObject(),builder), чтобы сформировать вложенный процесс анализа. Когда вложенная обработка завершится, анализ объекта будет полностью завершён.

Используя описанный выше процесс, фреймворк может генерировать отдельный класс вывода для каждого объекта, который предназначен для этого конкретного объекта. Класс вывода является собственным кодом, получение содержимого объекта и имён свойств осуществляется через собственные вызовы методов, что обеспечивает высокую производительность. Конечно, в процессе динамической компиляции есть много мест, требующих принятия решений. Например, если это объект, необходимо добавить {} в строку. Строки должны быть заключены в кавычки ("), а числа и логические значения — нет. Массивы требуют добавления [] и так далее. Но основная идея заключается в том, чтобы использовать динамическую компиляцию кода во время выполнения для создания класса вывода, предназначенного для конкретного объекта. Затем скомпилировать этот класс в файл класса с помощью Javassist. Пример сгенерированного кода выглядит следующим образом Сериализовать следующий объект

public class com.jfire.codejson.Home
{
``` ```
private String name   = "home";
private int    length = 113;
private int    width  = 89;
//省略get set方法
}

Содержание динамического класса, сгенерированного фреймворком для этого объекта:

public class JsonWriter_Home_231313131 {
    StringCache cache = (StringCache) $2;
    com.jfire.codejson.Home entity = (com.jfire.codejson.Home) $1;
    cache.append('{');
    cache.append("\"length\":").append(entity.getLength()).append(',');
    String name = entity.getName();
    if (name != null) {
        cache.append("\"name\":\"").append(name).append("\",");
    }
    cache.append("\"width\":").append(entity.getWidth()).append(',');
    if (cache.isCommaLast()) {
        cache.deleteLast();
    }
    cache.append('}');
}

Сериализация

Алгоритм сериализации работает следующим образом:

  1. Создаётся специальный сериализатор для вывода данных.

  2. В процессе сериализации используется класс StringCache, который выполняет роль буфера и накапливает данные в виде строки.

  3. Для каждого объекта вызывается метод get, который возвращает значение соответствующего поля.

  4. Полученные значения полей добавляются в строку с помощью метода append класса StringCache.

  5. После добавления всех значений строка закрывается фигурными скобками и выводится в качестве результата.

Десериализация

Разбор JSON-строки

Поскольку JSON — это структура «ключ-значение», алгоритм использует две стековые структуры:

  • стек ключей, в который помещаются ключи для обработки;
  • стек значений, в котором хранятся найденные объекты или массивы.

Процесс разбора JSON-строки включает следующие шаги:

  1. Создание двух стековых структур для хранения данных.
  2. Посимвольное чтение данных. При обнаружении специальных символов, таких как {, }, :, [, ], ,, ", выполняется специальная обработка, включая запись позиции обнаружения и определение необходимости создания нового объекта или массива.
  3. Повторение процесса чтения до конца строки. Если все символы были прочитаны, то возвращается верхнее значение из стека значений. Если JSON-строка не соответствует стандарту, это будет обнаружено в процессе разбора. Программа выдаст исключение.

Десериализация

Для преобразования JSON-объекта в Pojo используется аналогичный процесс:

  1. Получаются все методы set для Pojo.
  2. Компилируется динамический код. В начале кода создаётся объект типа Pojo, например, EntityClass entity = new EntityClass();
  3. Анализируются методы set. На основе имени метода определяется имя свойства. Затем строится код вида if(json.containsKey("name")){entity.setName(json.getString("name"))}
  4. После анализа всех методов set код компилируется в отдельный класс для преобразования. Этот класс добавляется в карту, где ключом является класс, который нужно преобразовать, а значением — класс преобразования. Если в процессе анализа обнаруживаются вложенные объекты, процесс повторяется для них. Например, если есть атрибут AnotherObject, код будет выглядеть так: if(json.containsKey("anotherObject")){entity.setAnotherObject(readContext.read(AnotherObject.class,json.getString("anotherObject")))}

После выполнения этих четырёх шагов генерируется код для преобразования класса. Использование этого кода позволяет быстро десериализовать объекты.

Комментарии ( 0 )

Вы можете оставить комментарий после Вход в систему

Введение

Описание недоступно Развернуть Свернуть
Отмена

Обновления

Пока нет обновлений

Участники

все

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/eric_ds-jfire-codejson.git
git@api.gitlife.ru:oschina-mirror/eric_ds-jfire-codejson.git
oschina-mirror
eric_ds-jfire-codejson
eric_ds-jfire-codejson
master