Рубрики
Без рубрики

Джексон против Джейсона

Краткое и практическое руководство по сериализации с помощью Jackson и Gson.

Автор оригинала: baeldung.

1. введение

В этой статье мы сравним API Gson и Jackson для сериализации и десериализации данных JSON с объектами Java и наоборот.

Gson и Jackson-это полные библиотеки, предлагающие поддержку привязки данных JSON для Java. В каждом из них активно разрабатываются проекты с открытым исходным кодом, которые предлагают обработку сложных типов данных и поддержку Java-дженериков.

И в большинстве случаев обе библиотеки могут десериализоваться в сущность без изменения класса сущности, что важно в тех случаях, когда разработчик не имеет доступа к исходному коду сущности.

2. Зависимость От Gson Maven


    com.google.code.gson
    gson
    ${gson.version}

Вы можете получить последнюю версию Gson здесь .

3. Сериализация Gson

Сериализация преобразует объекты Java в выходные данные JSON. Рассмотрим следующие объекты:

public class ActorGson {
    private String imdbId;
    private Date dateOfBirth;
    private List filmography;
    
    // getters and setters, default constructor and field constructor omitted
}

public class Movie {
    private String imdbId;
    private String director;
    private List actors;
    
    // getters and setters, default constructor and field constructor omitted
}

3.1. Простая сериализация

Давайте начнем с примера сериализации Java в JSON:

SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");

ActorGson rudyYoungblood = new ActorGson(
  "nm2199632",
  sdf.parse("21-09-1982"), 
  Arrays.asList("Apocalypto",
  "Beatdown", "Wind Walkers")
);
Movie movie = new Movie(
  "tt0472043", 
  "Mel Gibson",
  Arrays.asList(rudyYoungblood));

String serializedMovie = new Gson().toJson(movie);

Это приведет к:

{
    "imdbId": "tt0472043",
    "director": "Mel Gibson",
    "actors": [{
        "imdbId": "nm2199632",
        "dateOfBirth": "Sep 21, 1982 12:00:00 AM",
        "filmography": ["Apocalypto", "Beatdown", "Wind Walkers"]
    }]
}

По умолчанию:

  • Все свойства сериализуются, потому что они не имеют значений null
  • дата рождения поле было переведено с шаблоном данных Json по умолчанию
  • Выходные данные не форматируются, и имена свойств JSON соответствуют сущностям Java

3.2. Пользовательская сериализация

Использование пользовательского сериализатора позволяет нам изменять стандартное поведение. Мы можем ввести форматирование вывода с помощью HTML, обработать null значения, исключить свойства из вывода или добавить новый вывод.

ActorGsonSerializer изменяет генерацию кода JSON для элемента ActorGson :

public class ActorGsonSerializer implements JsonSerializer {
    private SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");
     
    @Override
    public JsonElement serialize(ActorGson actor, Type type,
        JsonSerializationContext jsonSerializationContext) {
        
        JsonObject actorJsonObj = new JsonObject();
        
        actorJsonObj.addProperty("IMDB Code", actor.getImdbId());
        
        actorJsonObj.addProperty("Date Of Birth", 
          actor.getDateOfBirth() != null ? 
          sdf.format(actor.getDateOfBirth()) : null);
        
        actorJsonObj.addProperty("N° Film: ",  
          actor.getFilmography()  != null ?  
          actor.getFilmography().size() : null);
       
        actorJsonObj.addProperty("filmography", actor.getFilmography() != null ? 
          convertFilmography(actor.getFilmography()) : null);
        
        return actorJsonObj;
    }
 
    private String convertFilmography(List filmography) {
        return filmography.stream()
          .collect(Collectors.joining("-"));
    }
}

Чтобы исключить свойство director , аннотация @Expose используется для свойств, которые мы хотим рассмотреть:

public class MovieWithNullValue {
    
    @Expose
    private String imdbId;
    private String director;
    
    @Expose
    private List actors;
}

Теперь мы можем приступить к созданию объекта Gson с помощью класса GsonBuilder :

Gson gson = new GsonBuilder()
  .setPrettyPrinting()
  .excludeFieldsWithoutExposeAnnotation()
  .serializeNulls()
  .disableHtmlEscaping()
  .registerTypeAdapter(ActorGson.class, new ActorGsonSerializer())
  .create();
 
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");
 
ActorGson rudyYoungblood = new ActorGson("nm2199632",
  sdf.parse("21-09-1982"), Arrays.asList("Apocalypto","Beatdown", "Wind Walkers"));

MovieWithNullValue movieWithNullValue = new MovieWithNullValue(null,
  "Mel Gibson", Arrays.asList(rudyYoungblood));
 
String serializedMovie = gson.toJson(movieWithNullValue);

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

{
  "imdbId": null,
  "actors": [
    {
      "IMDB Code": "nm2199632",
      "Date Of Birth": "21-09-1982",
      "N° Film: ": 3,
      "filmography": "Apocalypto-Beatdown-Wind Walkers"
    }
  ]
}

Обратите внимание, что:

  • вывод отформатирован
  • некоторые имена свойств изменены и содержат HTML
  • значения null включены, а поле director опущено
  • Дата теперь в формате дд-ММ-гггг
  • присутствует новое свойство – N° Film
  • фильмография-это форматированное свойство, а не список JSON по умолчанию

4. Десериализация Gson

4.1. Простая десериализация

Десериализация преобразует входные данные JSON в объекты Java. Чтобы проиллюстрировать вывод, мы реализуем метод toString() в обоих классах сущностей:

public class Movie {
    @Override
    public String toString() {
      return "Movie [imdbId=" + imdbId + ", director=" + director + ",actors=" + actors + "]";
    }
    ...
}

public class ActorGson {
    @Override
    public String toString() {
        return "ActorGson [imdbId=" + imdbId + ", dateOfBirth=" + dateOfBirth +
          ",filmography=" + filmography + "]";
    }
    ...
}

Затем мы используем сериализованный JSON и запускаем его через стандартную десериализацию Gson:

String jsonInput = "{\"imdbId\":\"tt0472043\",\"actors\":" +
  "[{\"imdbId\":\"nm2199632\",\"dateOfBirth\":\"1982-09-21T12:00:00+01:00\"," +
  "\"filmography\":[\"Apocalypto\",\"Beatdown\",\"Wind Walkers\"]}]}";
        
Movie outputMovie = new Gson().fromJson(jsonInput, Movie.class);
outputMovie.toString();

Выход-это мы, наши сущности, заполненные данными из нашего ввода JSON:

Movie [imdbId=tt0472043, director=null, actors=[ActorGson 
  [imdbId=nm2199632, dateOfBirth=Tue Sep 21 04:00:00 PDT 1982, 
  filmography=[Apocalypto, Beatdown, Wind Walkers]]]]

Как и в случае с простым сериализатором:

  • имена входных данных JSON должны соответствовать именам сущностей Java, иначе они будут иметь значение null.
  • поле дата рождения было переведено с шаблоном данных Json по умолчанию, игнорируя часовой пояс.

4.2. Пользовательская десериализация

Использование пользовательского десериализатора позволяет нам изменять стандартное поведение десериализатора. В этом случае мы хотим, чтобы дата отражала правильный часовой пояс для даты рождения . Для этого мы используем пользовательский десериализатор Actor Gson для сущности ActorGson :

public class ActorGsonDeserializer implements JsonDeserializer {

    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");

    @Override
    public ActorGson deserialize(JsonElement json, Type type,
      JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
        
        JsonObject jsonObject = json.getAsJsonObject();

        JsonElement jsonImdbId = jsonObject.get("imdbId");
        JsonElement jsonDateOfBirth = jsonObject.get("dateOfBirth");
        JsonArray jsonFilmography = jsonObject.getAsJsonArray("filmography");

        ArrayList filmList = new ArrayList();
        if (jsonFilmography != null) {
            for (int i = 0; i < jsonFilmography.size(); i++) {
                filmList.add(jsonFilmography.get(i).getAsString());
            }
        }

    ActorGson actorGson = new ActorGson(jsonImdbId.getAsString(),
      sdf.parse(jsonDateOfBirth.getAsString()), filmList);
        return actorGson;
    }
}

Мы использовали парсер SimpleDateFormat для анализа входных данных с учетом часового пояса.

Обратите внимание, что мы могли бы просто написать пользовательский десериализатор только для данных, но десериализатор Actor Gson предлагает более подробное представление процесса десериализации.

Также обратите внимание, что подход Json не требует изменения сущности ActorGson , что идеально, поскольку мы не всегда можем иметь доступ к входной сущности. Здесь мы используем пользовательский десериализатор:

String jsonInput = "{\"imdbId\":\"tt0472043\",\"actors\":"
  + "[{\"imdbId\":\"nm2199632\",\"dateOfBirth\":\"1982-09-21T12:00:00+01:00\",
  + \"filmography\":[\"Apocalypto\",\"Beatdown\",\"Wind Walkers\"]}]}";

Gson gson = new GsonBuilder()
  .registerTypeAdapter(ActorGson.class,new ActorGsonDeserializer())
  .create();

Movie outputMovie = gson.fromJson(jsonInput, Movie.class);
outputMovie.toString();

Результат аналогичен результату simpledeserializer, за исключением того, что дата использует правильный часовой пояс:

Movie [imdbId=tt0472043, director=null, actors=[ActorGson
  [imdbId=nm2199632, dateOfBirth=Tue Sep 21 12:00:00 PDT 1982, 
  filmography=[Apocalypto, Beatdown, Wind Walkers]]]]

5. Зависимость от Джексона Мэйвена

 
    com.fasterxml.jackson.core 
    jackson-databind   
    ${jackson.version} 

Вы можете получить последнюю версию Jackson здесь .

6. Сериализация Джексона

6.1. Простая сериализация

Здесь мы будем использовать Джексона для получения того же сериализованного контента, который мы имели с Gson, используя следующие сущности. Обратите внимание, что геттеры/сеттеры сущности должны быть общедоступными:

public class ActorJackson {
    private String imdbId;
    private Date dateOfBirth;
    private List filmography;
    
    // required getters and setters, default constructor 
    // and field constructor details omitted
}

public class Movie {
    private String imdbId;
    private String director;
    private List actors;
    
    // required getters and setters, default constructor 
    // and field constructor details omitted
}
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy"); 
ActorJackson rudyYoungblood = new ActorJackson("nm2199632",sdf.parse("21-09-1982"),
  Arrays.asList("Apocalypto","Beatdown","Wind Walkers") ); 
Movie movie = new Movie("tt0472043","Mel Gibson", Arrays.asList(rudyYoungblood)); 
ObjectMapper mapper = new ObjectMapper(); 
String jsonResult = mapper.writeValueAsString(movie);

Результат выглядит следующим образом:

{"imdbId":"tt0472043","director":"Mel Gibson","actors":
[{"imdbId":"nm2199632","dateOfBirth":401439600000,
"filmography":["Apocalypto","Beatdown","Wind Walkers"]}]}

Некоторые интересные заметки:

  • ObjectMapper является нашим сериализатором/десериализатором Джексона
  • Выходной JSON не отформатирован
  • По умолчанию дата Java переводится в long value

6.2. Пользовательская сериализация

Мы можем создать сериализатор Джексона для Актера Джексона генерации элементов, расширив StdSerializer для нашей сущности. Еще раз обратите внимание, что геттеры/сеттеры сущностей должны быть общедоступными:

public class ActorJacksonSerializer extends StdSerializer {

    private SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");

    public ActorJacksonSerializer(Class t) {
        super(t);
    }

    @Override
    public void serialize(ActorJackson actor, JsonGenerator jsonGenerator,
      SerializerProvider serializerProvider) throws IOException {

        jsonGenerator.writeStartObject();
        jsonGenerator.writeStringField("imdbId", actor.getImdbId());
        jsonGenerator.writeObjectField("dateOfBirth",
          actor.getDateOfBirth() != null ?
          sdf.format(actor.getDateOfBirth()) : null);
    
        jsonGenerator.writeNumberField("N° Film: ", 
          actor.getFilmography() != null ? actor.getFilmography().size() : null);
    jsonGenerator.writeStringField("filmography", actor.getFilmography()
          .stream().collect(Collectors.joining("-")));

        jsonGenerator.writeEndObject();
    }
}

Мы создаем объект фильма, чтобы разрешить игнорирование поля режиссер :

public class MovieWithNullValue {
    
    private String imdbId;
    
    @JsonIgnore
    private String director;
    
    private List actors;
    
    // required getters and setters, default constructor
    // and field constructor details omitted
}

Теперь мы можем приступить к созданию и настройке пользовательского ObjectMapper :

SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");

ActorJackson rudyYoungblood = new ActorJackson(
  "nm2199632", 
  sdf.parse("21-09-1982"), 
  Arrays.asList("Apocalypto", "Beatdown","Wind Walkers"));
MovieWithNullValue movieWithNullValue = 
  new MovieWithNullValue(null,"Mel Gibson", Arrays.asList(rudyYoungblood));

SimpleModule module = new SimpleModule();
module.addSerializer(new ActorJacksonSerializer(ActorJackson.class));
ObjectMapper mapper = new ObjectMapper();
String jsonResult = mapper.registerModule(module)
  .writer(new DefaultPrettyPrinter())
  .writeValueAsString(movieWithNullValue);

Выходные данные отформатированы в формате JSON, который обрабатывает значения null , форматирует данные, исключает поле director и показывает новый вывод :

{
  "actors" : [ {
    "imdbId" : "nm2199632",
    "dateOfBirth" : "21-09-1982",
    "N° Film: " : 3,
    "filmography" : "Apocalypto-Beatdown-Wind Walkers"
  } ],
  "imdbID" : null
}

7. Десериализация Джексона

7.1. Простая десериализация

Чтобы проиллюстрировать вывод, мы реализуем метод toString() в обоих классах сущностей Джексона:

public class Movie {
    @Override
    public String toString() {
        return "Movie [imdbId=" + imdbId + ", director=" + director
          + ", actors=" + actors + "]";
    }
    ...
}

public class ActorJackson {
    @Override
    public String toString() {
        return "ActorJackson [imdbId=" + imdbId + ", dateOfBirth=" + dateOfBirth
          + ", filmography=" + filmography + "]";
    }
    ...
}

Затем мы используем сериализованный JSON и запускаем его через десериализацию Джексона:

String jsonInput = "{\"imdbId\":\"tt0472043\",\"actors\":
  [{\"imdbId\":\"nm2199632\",\"dateOfBirth\":\"1982-09-21T12:00:00+01:00\",
  \"filmography\":[\"Apocalypto\",\"Beatdown\",\"Wind Walkers\"]}]}";
ObjectMapper mapper = new ObjectMapper();
Movie movie = mapper.readValue(jsonInput, Movie.class);

Выход-это мы, наши сущности, заполненные данными из нашего ввода JSON:

Movie [imdbId=tt0472043, director=null, actors=[ActorJackson 
  [imdbId=nm2199632, dateOfBirth=Tue Sep 21 04:00:00 PDT 1982, 
  filmography=[Apocalypto, Beatdown, Wind Walkers]]]]

Как и в случае с простым сериализатором:

  • имена входных данных JSON должны соответствовать именам сущностей Java, или они имеют значение null,
  • Поле DateOfBirth было переведено с шаблоном даты Джексона по умолчанию, игнорируя часовой пояс.

7.2. Пользовательская десериализация

Использование пользовательского десериализатора позволяет нам изменять стандартное поведение десериализатора.

В этом случае мы хотим, чтобы данные отражали правильный часовой пояс для даты рождения, поэтому мы добавляем DateFormatter в наш файл ObjectMapper :

String jsonInput = "{\"imdbId\":\"tt0472043\",\"director\":\"Mel Gibson\",
  \"actors\":[{\"imdbId\":\"nm2199632\",\"dateOfBirth\":\"1982-09-21T12:00:00+01:00\",
  \"filmography\":[\"Apocalypto\",\"Beatdown\",\"Wind Walkers\"]}]}";

ObjectMapper mapper = new ObjectMapper();
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
mapper.setDateFormat(df);
        
Movie movie = mapper.readValue(jsonInput, Movie.class);
movie.toString();

Выходные данные отражают правильный часовой пояс с датой:

Movie [imdbId=tt0472043, director=Mel Gibson, actors=[ActorJackson 
  [imdbId=nm2199632, dateOfBirth=Tue Sep 21 12:00:00 PDT 1982, 
  filmography=[Apocalypto, Beatdown, Wind Walkers]]]]

Это решение является чистым и простым.

В качестве альтернативы мы могли бы создать пользовательский десериализатор для класса Actor Jackson , зарегистрировать этот модуль в нашем ObjectMapper и десериализовать дату с помощью аннотации @ JsonDeserialize на сущности ActorJackson .

Недостатком такого подхода является необходимость изменения сущности, что может быть не идеальным для случаев, когда у нас нет доступа к входным классам сущностей.

8. Заключение

И Gson, и Jackson являются хорошими вариантами для сериализации/десериализации данных JSON, простыми в использовании и хорошо документированными.

Преимущества Gson:

  • Простота toJSON |//FromJSON в простых случаях Для десериализации не требуется доступ к сущностям Java

Преимущества Джексона:

  • Встроенный во все JAX-RS (Jersey, Apache CXF, RESTEasy, Restlet) и Spring framework
  • Обширная поддержка аннотаций

Вы можете найти код для Gson и Jackson на GitHub.