Jfire-codejson — это фреймворк с высочайшей производительностью для работы с JSON.
JsonTool.write(entity)
. Для десериализации — JsonTool.read(User.class, str)
.Предположим, что есть несколько классов:
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.JsonTool.fromString(str)
, чтобы преобразовать строку JSON в объект JSON (может быть JsonObject
или JsonArray
).JsonTool.read(User.class,str)
, чтобы преобразовать строку JSON в объект класса User
.@JsonIgnore
и @JsonRename
. При сериализации аннотацию можно разместить на методе get
, а при десериализации — на методе set
. Если аннотация размещена на атрибуте, она будет применяться как при сериализации, так и при десериализации. Аннотации игнорирования не действуют в стратегиях, если стратегия не включает игнорирование этого атрибута. Чтобы принудительно игнорировать атрибут, используйте @JsonIgnore(force=true)
.WriteStrategy strategy = new WriteStrategy()
.strategy.addIgnoreField("link.jfire.test.User.age")
, где формат параметра — класс.атрибут
.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("}");
}
В приведённом выше примере кода нет отражения или анализа. Он представляет собой конкретный код сериализации, предназначенный для данного класса. Если наш фреймворк также может имитировать этот подход, мы можем достичь максимальной скорости сериализации и приблизиться к теоретическому верхнему пределу. Чтобы достичь этого эффекта, используется динамическая компиляция кода.
builder.append("name").append(":\"").append(entity.getName()).append("\",
)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('}');
}
Алгоритм сериализации работает следующим образом:
Создаётся специальный сериализатор для вывода данных.
В процессе сериализации используется класс StringCache, который выполняет роль буфера и накапливает данные в виде строки.
Для каждого объекта вызывается метод get, который возвращает значение соответствующего поля.
Полученные значения полей добавляются в строку с помощью метода append класса StringCache.
После добавления всех значений строка закрывается фигурными скобками и выводится в качестве результата.
Поскольку JSON — это структура «ключ-значение», алгоритм использует две стековые структуры:
Процесс разбора JSON-строки включает следующие шаги:
{
, }
, :
, [
, ]
, ,
, "
, выполняется специальная обработка, включая запись позиции обнаружения и определение необходимости создания нового объекта или массива.Для преобразования JSON-объекта в Pojo используется аналогичный процесс:
EntityClass entity = new EntityClass();
if(json.containsKey("name")){entity.setName(json.getString("name"))}
if(json.containsKey("anotherObject")){entity.setAnotherObject(readContext.read(AnotherObject.class,json.getString("anotherObject")))}
После выполнения этих четырёх шагов генерируется код для преобразования класса. Использование этого кода позволяет быстро десериализовать объекты.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )