看到一篇介绍jackson和gson的文章,觉得不错,翻译过来分享给大家。原文:jackson-vs-gson
1. Introduction
In this article, we’ll compare the Gson and Jackson APIs for serializing and deserializing JSON data to Java objects and vice-versa.
Gson和Jackson的API都可以用来将java 对象序列化成JSON数据,或者将JSON数据反序列化为java对象。在这篇文章里,我们将把他们放在一起进行比较。
Gson and Jackson are complete libraries offering JSON data-binding support for Java. Each are actively developed open-source projects which offer to handle of complex data types and support for Java Generics.
Gson和Jackson都为java提供了完整的用于JSON数据绑定的类库。他们都是活跃的开源工程,支持java 操作复杂的数据类型。
And in most cases, both libraries can deserialize to an entity without modifying an entity class, which is important in cases where a developer doesn’t have access to the entity source code.
在大多数情况,这两个类库都可以在不改变实体类的同时将数据凡序列化实体。在开发人员没有访问实体源代码的时候,这一点尤其重要。
2. Gson Maven Dependency
com.google.code.gson
gson
${gson.version}
You can get the latest version of Gson here.
最新版本的Gson。
3. Gson Serialization
Serialization converts Java objects to JSON output. Consider the following entities:
序列化将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. Simple Serialization
Let’s start with an example of Java to JSON serialization:
让我门首先看一个将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);
This will result in:
程序运行结果:
{
"imdbId": "tt0472043",
"director": "Mel Gibson",
"actors": [{
"imdbId": "nm2199632",
"dateOfBirth": "Sep 21, 1982 12:00:00 AM",
"filmography": ["Apocalypto", "Beatdown", "Wind Walkers"]
}]
}
By default:
- All properties are serialized because they have no null values
- dateOfBirth field was translated with the default Gson date pattern
- Output is not formatted and JSON property names correspond to the Java entities
默认条件下,
- 所有的值非null的属性都被序列化
- dateOfBirth filed 被转化成了Gson的默认date 格式。
- 输出并没有被格式化,JSON属性的名字和java实体对象一致。
3.2. Custom Serialization
Using a custom serializer allows us to modify the standard behavior. We can introduce an output formatter with HTML, handle null values, exclude properties from output, or add a new output.
通过使用一个定制化的序列化器,我门可以更改标准的行为。例如,引入一个HTML格式的输出器,处理null值,将某些属性从输出中排除,或者添加一个新的输出。
ActorGsonSerializer modifies generation of JSON code for the ActorGson element:
ActorGsonSerializer为ActorGson对象修改了JSON 数据的生成结果:
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("-"));
}
}
In order to exclude the *director *property, the @Expose annotation is used for properties we want to consider:
需要排除的director属性可以用@Expose 标注:
public class MovieWithNullValue {
@Expose
private String imdbId;
private String director;
@Expose
private List actors;
}
Now we can proceed with Gson object creation using the GsonBuilder class:
现在我们可以用类GsonBuilder创建Gson对象:
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);
The result is the following:
结果如下:
{
"imdbId": null,
"actors": [
{
"IMDB Code": "nm2199632", "Date Of Birth": "21-09-1982",
"N° Film: ": 3,
"filmography": "Apocalypto-Beatdown-Wind Walkers"
}
]
}
Notice that:
注意到:
- the output is formatted
- some property names are changed and contain HTML
- null values are included, and the *director *field is omitted
- Date is now in the dd-MM-yyyy format
- a new property is present – N° Film
- filmography is a formatted property, not the default JSON list
- 输出是格式化的
- 一些属性名被改变,包含了HTML
- 输出了null值,字段 director被删除了
- Date的格式为dd-MM-yyyy
- 出现了一个新的属性 - N° Film
- filmography是一个格式化的属性,而不是默认的JSON列表。
4. Gson Deserialization
4.1. Simple Deserialization
Deserialization converts JSON input into Java objects. To illustrate the output, we implement the toString() method in both entity classes:
反序列化将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 + "]";
}
...
}
Then we utilize the serialized JSON and run it through standard Gson deserialization:
接下来,我们用标准的方式反序列化已经被序列化的JSON数据。
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();
The output is us our entities, populated with the data from our JSON input:
输出的结果就是我们的实体。
Movie [imdbId=tt0472043, director=`null`, actors=[ActorGson
[imdbId=nm2199632, dateOfBirth=Tue Sep 21 04:00:00 PDT 1982,
filmography=[Apocalypto, Beatdown, Wind Walkers]]]]
As was the case with the simple serializer:
- the JSON input names must correspond with the Java entity names, or they are set to null.
- dateOfBirth field was translated with the default Gson date pattern, ignoring the time zone.
同简单的序列化器一样:
- JSON 输入的属性名必须和java实体的属性名相同,否则他们将被致成null。
- dateOfBirth字段被转化成了默认的Gson date 格式,忽略了时区。
4.2. Custom Deserialization
Using a custom deserializer allows us to modify the standard deserializer behavior. In this case, we want the date to reflect the correct time zone for dateOfBirth. We use a custom ActorGsonDeserializer on the ActorGson entity to achieve this:
通过使用一个定制化的反序列器,可以修改标准反序列化器的行为。在这种情况下,我们想让dateOfBirth内设置的时间反应出正确的时区。为了达到这个目的,我们为ActorGson实体使用一个定制化的ActorGsonDeserializer:
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;
}
}
We employed a SimpleDateFormat parser to parse the input date, accounting for the time zone.
Note that we could have decided to simply write a custom deserializer for only the Date, but the ActorGsonDeserializer offers a more detailed view of the deserialization process.
Also note that the Gson approach does not require modifying the *ActorGson *entity, which is ideal as we may not always have access to the input entity. We use the custom deserializer here:
我们部署了一个SimpleDateFormat parser用以解析输入的date数据,它会考虑时区信息。
注意到我们可能已经决定仅仅为date定制化一个反序列器,但是ActorGsonDeserializer为反序列化过程提供了一个更加详细的视角。
同时,注意到Gson解决方案并不要求修改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();
The output is similar to the simple deserializer result, except the date uses correct time zone:
输出和标准的反序列化器的结果类似,只是这里的date用的是正确的时区信息:
Movie [imdbId=tt0472043, director=null, actors=[ActorGson
[imdbId=nm2199632, dateOfBirth=Tue Sep 21 12:00:00 PDT 1982,
filmography=[Apocalypto, Beatdown, Wind Walkers]]]]
5. Jackson Maven Dependency
com.fasterxml.jackson.core
jackson-databind
${jackson.version}
You can get the latest version of Jackson here.
这里可以获得Jackson的最新版本。
6. Jackson Serialization
6.1. Simple Serialization
Here we will use Jackson to obtain the same serialized content we had with Gson using the following entities. Note that the entity’s getters/setters must be public:
这里我们将用Jackson来获得以下实体的序列化内容,这些内容与之前用Gson获得的一样,注意到实体的getters/setters必须是public
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);
The output is as follows:
输出结构如下所示:
{"imdbId":"tt0472043","director":"Mel Gibson","actors":
[{"imdbId":"nm2199632","dateOfBirth":401439600000,
"filmography":["Apocalypto","Beatdown","Wind Walkers"]}]}
Some notes of interest:
- ObjectMapper is our Jackson serializer/deserializer
- The output JSON is not formatted
- By default, Java Date is translated to *long *value
一些值得注意的点:
- ObjectMapper是我们的序列化器/反序列化器
- 输出的JSON数据并没有格式化
- 默认情况下,java Date被转化为long的值
6.2. Custom Serialization
We can create a Jackson serializer for ActorJackson element generation by extending StdSerializer for our entity. Again note that the entity getters/setters must be public:
为了生成JSON数据,我们可以扩展StdSerializer,为ActorJackson创建一个Jackson序列化器,
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();
}
}
We create a Movie entity to allow ignoring of the director field:
我们创建一个可以忽略director字段的Movie实体:
public class MovieWithNullValue {
private String imdbId;
@JsonIgnore
private String director;
private List actors;
// required getters and setters, default constructor
// and field constructor details omitted
}
Now we can proceed with a custom *ObjectMapper *creation and setup:
现在,我们可以继续定制化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);
The output is formatted JSON that handles null values, formats the date, excludes the directorfield and shows new output of N°:
输出的是格式化的JSON数据,它处理了null值,格式化了date,排除了director字段,而且N°有了新的输出
"actors" : [ {
"imdbId" : "nm2199632",
"dateOfBirth" : "21-09-1982",
"N° Film: " : 3,
"filmography" : "Apocalypto-Beatdown-Wind Walkers"
} ],
"imdbID" : null
}
7. Jackson Deserialization
7.1. Simple Deserialization
To illustrate the output, we implement the toString() method in both Jackson entity classes:
为了演示输出,我们为Jackson实体类实现了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 + "]";
}
...
}
Then we utilize the serialized JSON and run it through Jackson deserialization:
然后我们就可以Jackson反序列化已经序列化的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);
The output is us our entities, populated with the data from our JSON input:
输出就是我们的实体,以输入的JSON数据进行了填充。
Movie [imdbId=tt0472043, director=null, actors=[ActorJackson
[imdbId=nm2199632, dateOfBirth=Tue Sep 21 04:00:00 PDT 1982,
filmography=[Apocalypto, Beatdown, Wind Walkers]]]]
As was the case with the simple serializer:
- the JSON input names must correspond with the Java entity >names, or they are set to null,
- dateOfBirth field was translated with the default Jackson date pattern, ignoring the time zone.
同简单的序列化器的情况一样:
*JSON输入的名字必须和Java实体的名字相同,否则的话他们将被设置为null,
*dateOfBirth字段被转化为了默认的Jackson date格式,不考虑时区信息。
7.2. Custom Deserialization
Using a custom deserializer allows us to modify the standard deserializer behavior.
In this case, we want the date to reflect the correct time zone for *dateOfBirth, *so we add a DateFormatter to our Jackson ObjectMapper:
使用一个定制化的反序列化器,我们可以修改标准反序列化器的行为。
在这种情况下,我们想要date为dateOfBirth反应出正确的时区信息,所以我们为Jackson ObjectMapper加入了一个DateFormatter:
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-ddTHH:mm:ss");
mapper.setDateFormat(df);
Movie movie = mapper.readValue(jsonInput, Movie.class);
movie.toString();
The output reflects the correct time zone with the date:
输出为date反应了正确的时区:
Movie [imdbId=tt0472043, director=Mel Gibson, actors=[ActorJackson
[imdbId=nm2199632, dateOfBirth=Tue Sep 21 12:00:00 PDT 1982,filmography=[Apocalypto, Beatdown, Wind Walkers]]]]
This solution is clean and simple.
Alternatively, we could have created a custom deserializer for the ActorJackson class, registered this module with our ObjectMapper, and deserialized the date using the @JsonDeserialize annotation on the ActorJackson entity.
The disadvantage of that approach is the need to modify the entity, which may not be ideal for cases when we don’t have access to the input entity classes.
这一解决方案简洁明了。
或者,我们可以为ActorJackson类创建定制化的反序列化器,并将module注册到ObjectMapper上,并反序列化在ActorJackson实体中用@JsonDeserialize标注的date。
这一解决方案的缺点就是需要修改实体,当我们不能访问实体类的时候,这不是一个完美的解决方案。
8. Conclusion
Both Gson and Jackson are good options for serializing/deserializing JSON data, simple to use and well documented.
对于序列化/反序列化来说,Gson和Jackson都是不错的选择,使用简单,文档完备。
Advantages of Gson:
- Simplicity of toJson/fromJson in the simple cases
- For deserialization, do not need access to the Java entities
Gson的优点:
- toJson/fromJson的简洁性。
- 反序列化不需要访问java实体。
Advantages of Jackson:
- Built into all JAX-RS (Jersey, Apache CXF, RESTEasy, Restlet), and Spring framework
- Extensive annotation support
Jackson的优点:
*内嵌于all JAX-RS (Jersey, Apache CXF, RESTEasy, Restlet), 和Spring 框架。
*支持扩展的标注。
You can find the code for Gson and Jackson on GitHub.