1. 簡介
本文將比較 Gson 和 Jackson API,用於將 JSON 數據序列化為 Java 對象,以及反序列化 Java 對象為 JSON 數據。
Gson 和 Jackson 都是完整的庫,提供 JSON 數據綁定支持,用於 Java。它們都是積極開發中的開源項目,可以處理複雜的數據類型並支持 Java 泛型。
在大多數情況下,這兩個庫都可以無需修改實體類即可反序列化到實體中,這在開發者無法訪問實體源代碼的情況下非常重要。
2. Gson Maven 依賴
Gson 是一個用於 Java 的 JSON 數據處理庫。要使用 Gson,您需要將其添加到您的 Maven 項目中。以下是如何添加 Gson Maven 依賴的步驟:
在您的 pom.xml 文件中,添加以下依賴聲明:
<dependency>
<groupId>com.google.code</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version> <!-- 請根據最新版本號進行更新 -->
</dependency>
解釋:
groupId:com.google.code是 Gson 的 groupId。artifactId:gson是 Gson 的 artifactId。version:2.8.6是 Gson 的版本號。 請務必使用最新版本,以獲取最新的功能和修復。
驗證依賴是否已成功添加:
Maven 會自動下載並管理 Gson 依賴。您可以通過以下方式驗證依賴是否已成功添加:
- 在 Maven 項目的構建過程中,檢查構建日誌,確認 Gson 依賴已成功下載。
- 在您的 Java 代碼中,使用 Gson 類,例如:
import com.google.gson.Gson;
public class GsonExample {
public static void main(String[] args) {
// ...
}
}
確保您的項目能夠正常編譯和運行,這表明 Gson 依賴已正確配置。
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>您可以在這裏獲取 Gson 的最新版本:https://mvnrepository.com/artifact/com.google.code.gson/gson。
3. Gson 序列化
序列化將 Java 對象轉換為 JSON 輸出。請考慮以下實體:
public class ActorGson {
private String imdbId;
private Date dateOfBirth;
private List<String> filmography;
// getters and setters, default constructor and field constructor omitted
}
public class Movie {
private String imdbId;
private String director;
private List<ActorGson> 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 值
- dateOfBirth 字段已使用 Gson 默認日期模式進行翻譯
- 輸出未進行格式化,JSON 屬性名稱與 Java 實體相對應
3.2. 自定義序列化
使用自定義序列化器允許我們修改標準行為。我們可以引入 HTML 輸出格式器,處理 null 值,排除輸出屬性,或添加新的輸出。
ActorGsonSerializer 修改了 ActorGson 元素的 JSON 代碼生成:
public class ActorGsonSerializer implements JsonSerializer<ActorGson> {
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<String> filmography) {
return filmography.stream()
.collect(Collectors.joining("-"));
}
}為了排除 director 屬性,我們會使用 @Expose 註解來標記我們希望被考慮的屬性:
public class MovieWithNullValue {
@Expose
private String imdbId;
private String director;
@Expose
private List<ActorGson> actors;
}現在我們可以使用 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);結果如下:
{
"imdbId": null,
"actors": [
{
"IMDB Code": "nm2199632",
"Date Of Birth": "21-09-1982",
"N° Film: ": 3,
"filmography": "Apocalypto-Beatdown-Wind Walkers"
}
]
}請注意以下事項:
- 輸出已格式化
- 某些屬性名稱已更改,包含 HTML
- null 值已包含,且 director 字段已省略
- Date 格式現在為 dd-MM-yyyy
- 新增一個屬性 – N° Film
- 電影列表是一個格式化屬性,而不是默認的 JSON 列表
4. Gson 序列化
Gson 序列化是將 Java 對象轉換為 JSON 字符串的過程。Gson 庫提供了方便的 API 來執行此操作,並允許您控制 JSON 字符串的格式。
4.1. 簡單反序列化
反序列化將 JSON 輸入轉換為 Java 對象。為了説明輸出結果,我們在實體類中實現了 <em>toString()</em> 方法:
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。
- dateOfBirth 字段已使用 Gson 默認日期模式進行翻譯,忽略時區信息。
4.2. 自定義反序列化
使用自定義反序列器允許我們修改標準反序列器的行為。在本例中,我們希望 出生日期 能夠反映正確的時區。我們通過在 <em ActorGson 實體上使用自定義 <em ActorGsonDeserializer 來實現這一點:
public class ActorGsonDeserializer implements JsonDeserializer<ActorGson> {
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<String> filmList = new ArrayList<String>();
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 解析器來解析輸入日期,並考慮了時區。
請注意,我們也可以選擇編寫一個僅針對 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();輸出類似於簡單的反序列化結果,但日期使用了正確的時區:
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 依賴
要將 Jackson 庫集成到您的 Maven 項目中,您需要添加相應的依賴項。以下是配置 Maven 依賴項的步驟:
1. 添加依賴項:
在您的 pom.xml 文件中,添加以下依賴項:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.13.4</version> <!-- 請根據您的項目需求選擇最新版本 -->
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.4</version> <!-- 請根據您的項目需求選擇最新版本 -->
</dependency>
解釋:
jackson-core: Jackson 核心庫,提供 Jackson 的基礎功能。jackson-databind: Jackson 數據綁定庫,提供將 Java 對象轉換為 JSON 和反之的功能。
2. 確保版本兼容性:
請務必選擇與您的 Jackson 版本兼容的依賴項版本。建議您使用 Maven 的依賴管理功能,以便 Maven 可以自動下載和管理依賴項。
3. 驗證依賴項:
在 Maven 構建完成後,請確保依賴項已正確下載並添加到您的項目中。您可以使用 Maven 的依賴管理工具來驗證依賴項是否已正確配置。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.2</version>
</dependency>您可以在 這裏 獲取 Jackson 的最新版本。
6. Jackson 序列化
Jackson 序列化是 Jackson 庫的核心功能之一,它允許將 Java 對象轉換為 JSON 格式的數據。 這種轉換對於在 Web 應用程序中進行數據交換、存儲到 NoSQL 數據庫或與其他系統進行集成至關重要。
Jackson 提供了多種序列化策略,允許開發者控制對象轉換的過程。 常見的序列化策略包括:
- DefaultSerializer: 默認序列化器,用於將基本 Java 類型(如 int、long、String、boolean)轉換為 JSON 格式。
- Property-based Serializer: 基於屬性的序列化器,允許開發者自定義屬性的序列化方式。
- Field-aware Serializer: 感知字段的序列化器,可以根據字段的註解進行序列化。
通過使用 Jackson 序列化,開發者可以輕鬆地將 Java 對象轉換為 JSON 格式,並將其用於各種 Web 應用程序和數據交換場景中。
6.1. 簡單序列化
這裏我們將使用 Jackson 來獲取與 Gson 使用相同實體時獲得的相同序列化內容,如下所示。請注意,實體的 getter/setter 方法必須是 public:
public class ActorJackson {
private String imdbId;
private Date dateOfBirth;
private List<String> filmography;
// required getters and setters, default constructor
// and field constructor details omitted
}
public class Movie {
private String imdbId;
private String director;
private List<ActorJackson> 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 是我們的 Jackson 序列化器/反序列器
- 輸出的 JSON 不進行格式化
- 默認情況下,Java Date 會轉換為 long 值
6.2. 自定義序列化
我們可以為 <em >ActorJackson</em> 元素生成創建 StdSerializer,通過擴展我們的實體來創建自定義 Jackson 序列化器。請注意,實體 getter/setter 方法必須是公開的。
public class ActorJacksonSerializer extends StdSerializer<ActorJackson> {
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();
}
}我們創建一個 Movie 實體,以便忽略 導演 字段:
public class MovieWithNullValue {
private String imdbId;
@JsonIgnore
private String director;
private List<ActorJackson> 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);<p>輸出格式為 JSON,處理 <em >null</em> 值,格式化日期,排除 <em >director</em> 字段,並顯示新的 <em >N°</em>:</p>
{
"actors" : [ {
"imdbId" : "nm2199632",
"dateOfBirth" : "21-09-1982",
"N° Film: " : 3,
"filmography" : "Apocalypto-Beatdown-Wind Walkers"
} ],
"imdbID" : null
}7. Jackson 序列化解構
Jackson 序列化解構是理解 Jackson 框架的核心概念。 序列化是將 Java 對象轉換為 JSON 格式的過程,而解構則是將 JSON 數據反向映射回 Java 對象的過程。
7.1 序列化 (Serialization)
序列化允許你將 Java 對象轉換為 JSON 格式,以便在網絡上傳輸或存儲到文件中。 Jackson 提供了強大的序列化支持,可以自動處理各種 Java 數據類型,包括基本類型、自定義對象和集合。
7.2 解構 (Deserialization)
解構允許你將 JSON 數據反向映射回 Java 對象。 Jackson 提供了靈活的解構機制,可以根據 JSON 結構自動創建 Java 對象,並設置其屬性值。
7.3 示例代碼
以下是一個簡單的示例,演示了 Jackson 的序列化和解構功能:
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonExample {
public static void main(String[] args) throws Exception {
// 序列化
Person person = new Person("John Doe", 30);
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeValueAsString(person);
System.out.println(jsonString);
// 解構
Person deserializedPerson = mapper.readValue(jsonString, Person.class);
System.out.println(deserializedPerson);
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
7.1. 簡單反序列化
為了説明輸出結果,我們在 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 + "]";
}
...
}然後,我們利用序列化的JSON數據並將其通過Jackson反序列化:
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 字段已使用默認的 Jackson 日期格式進行翻譯,忽略時區信息。
7.2. 自定義反序列化
使用自定義反序列器允許我們修改標準反序列器的行為。
在這種情況下,我們希望日期反映出正確的時區,因此我們在 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-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]]]]該解決方案簡潔明瞭。
或者,我們也可以為 ActorJackson 類創建一個自定義的反序列器,將該模塊註冊到我們的 ObjectMapper 中,並使用 @JsonDeserialize 註解來反序列化日期,應用於 ActorJackson 實體。
這種方法的缺點是需要修改實體,這在當沒有訪問輸入實體類時可能不是理想的選擇。
8. 結論
Gson 和 Jackson 都是用於序列化/反序列化 JSON 數據的良好選擇,易於使用且文檔完善。Gson 的優點:
- 在簡單情況下,toJson/fromJson 的簡潔性
- 在反序列化時,不需要訪問 Java 實體
Jackson 的優點:
- 內置於所有 JAX-RS (Jersey, Apache CXF, RESTEasy, Restlet) 和 Spring 框架
- 廣泛的註解支持