反序列化為每個值具有正確類型的映射
1. 簡介
反序列化是將資料從一種格式(例如 JSON、XML 或位元組)轉換回 Java 物件的過程。當我們反序列化為Map<String, Object>
時,我們希望 Map 中的每個值都具有其正確的 Java 類型——不僅僅是字串,還包括實際的整數、布林值、陣列和巢狀物件。
在本教程中,我們將學習如何使用幾種不同的方法正確地反序列化數據,同時保留每個欄位的原始資料類型。
2. 使用 Jackson 庫
Jackson 是 Java 中使用最廣泛的 JSON 處理庫。它提供了快速可靠的反序列化功能,並具有出色的類型保存功能。
要使用Jackson ,我們首先需要將依賴項新增到我們的pom.xml
檔案中:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.19.2</version>
</dependency>
以下是我們如何使用 Jackson 來實現這一點:
public class JacksonDeserializer {
private final ObjectMapper objectMapper;
public JacksonDeserializer() {
this.objectMapper = new ObjectMapper();
}
public Map<String, Object> deserializeToMapUsingJackson(String jsonString) {
try {
TypeReference<Map<String, Object>> typeRef = new TypeReference<Map<String, Object>>() {};
Map<String, Object> result = objectMapper.readValue(jsonString, typeRef);
return result;
} catch (Exception e) {
throw new RuntimeException("Failed to deserialize JSON: " + e.getMessage(), e);
}
}
}
在這個例子中,我們首先建立一個ObjectMapper
來處理 JSON。然後,我們使用TypeReference<Map<String, Object>>
來告訴 Jackson,我們想要將 JSON 反序列化為一個包含不同類型字串鍵和值的映射。
讓我們用一個範例 JSON 來測試我們的實作:
String json = """
{
"name": "John",
"age": 30,
"isActive": true,
"salary": 50000.75,
"hobbies": ["reading", "coding"],
"address": {
"street": "123 Main St",
"city": "New York"
}
}
""";
Map<String, Object> result = deserializeToMapUsingJackson(json);
assertEquals("John", result.get("name"));
assertEquals(30, result.get("age"));
assertEquals(true, result.get("isActive"));
assertEquals(50000.75, result.get("salary"));
assertTrue(result.get("hobbies") instanceof ArrayList);
此測試確認 JSON 中的每個欄位都被反序列化為正確的 Java 類型 - name
為String
, age
為Integer
, isActive
為Boolean
, salary
為Double
, hobbies
為ArrayList
。
Jackson 的主要優點是它非常快速且可靠,並且對處理不同類型的 JSON 具有出色的支援。
3.使用 Gson 函式庫
Gson 是另一個流行的 Java JSON 庫,由 Google 創建。它輕量級、易於使用,非常適合簡單的序列化和反序列化任務。
要使用Gson ,我們首先需要將依賴項新增到我們的pom.xml
檔案中:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.12.1</version>
</dependency>
以下是使用 Gson 進行反序列化的範例:
public class GsonDeserializer {
private final Gson gson;
public GsonDeserializer() {
this.gson = new Gson();
}
public Map<String, Object> deserializeToMapUsingGson(String jsonString) {
try {
Type type = new TypeToken<Map<String, Object>>() {}.getType();
Map<String, Object> result = gson.fromJson(jsonString, type);
return result;
} catch (Exception e) {
throw new RuntimeException("Failed to deserialize JSON: " + e.getMessage(), e);
}
}
}
首先,我們初始化一個 Gson 實例來處理 JSON 解析。使用TypeToken<Map<String, Object>>
,我們告訴 Gson 將 JSON 反序列化為 map,其中鍵是字串,值可以是任意型別。
讓我們用一個範例 JSON 來測試我們的實作:
Map<String, Object> result = deserializer.deserializeToMapUsingGson(json);
assertEquals("John", result.get("name"));
assertEquals(30.0, result.get("age"));
assertEquals(true, result.get("isActive"));
assertEquals(50000.75, result.get("salary"));
assertTrue(result.get("hobbies") instanceof ArrayList);
此測試顯示 Gson 成功將 JSON 反序列化為Map<String, Object>
。
然而,與 Jackson 不同的是,Gson 預設將所有數字視為Double
類型,因此age
和salary
都會被反序列化為Double
類型。如果我們需要嚴格的類型處理,這一點很重要。
Gson 的主要優點是它非常簡單輕量,因此在小型專案中易於建造。缺點是類型保存能力比 Jackson 弱,尤其是在數字方面。
4. 使用 org.json 函式庫
org.json 函式庫是一個輕量級且簡單的 Java JSON 處理工具。它易於使用,非常適合處理小型或簡單的 JSON 解析任務。
要使用org.json ,我們可以新增 Maven 依賴項:
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20250107</version>
</dependency>
使用 org.json,我們可以將 JSON 解析為一個映射,如下所示:
public class OrgJsonDeserializer {
public Map<String, Object> deserializeToMapUsingOrgJson(String jsonString) {
try {
JSONObject jsonObject = new JSONObject(jsonString);
Map<String, Object> result = new HashMap<>();
for (String key : jsonObject.keySet()) {
Object value = jsonObject.get(key);
if (value instanceof JSONArray) {
value = ((JSONArray) value).toList();
} else if (value instanceof JSONObject) {
value = ((JSONObject) value).toMap();
}
result.put(key, value);
}
return result;
} catch (Exception e) {
throw new RuntimeException("Failed to deserialize JSON: " + e.getMessage(), e);
}
}
}
在此範例中,我們建立一個JSONObject
來解析 JSON 字串。然後,我們迭代它的鍵,並轉換每個值。如果值是JSONArray
,我們會將其轉換為List
以便在 Java 中更輕鬆地處理。這為我們提供了一個Map<String, Object>
其中每個值可以是原始類型或列表。
讓我們用一個範例 JSON 來測試實作:
Map<String, Object> result = deserializer.deserializeToMapUsingOrgJson(json);
assertEquals("John", result.get("name"));
assertEquals(30, result.get("age"));
assertEquals(true, result.get("isActive"));
assertEquals(BigDecimal.valueOf(50000.75), result.get("salary"));
assertTrue(result.get("hobbies") instanceof List);
assertTrue(result.get("address") instanceof Map);
此測試顯示 org.json 成功將 JSON 轉換為Map<String, Object>
。與 Gson 不同,數值會保留為Integer
,並使用BigDecimal
來處理小數以保持精確度。這可以使混合數字的類型處理更加簡單。
然而,org.json 的主要限制是它不能像 Jackson 或 Gson 那樣自動處理複雜類型映射或嵌套物件。
對於小型專案或快速解析任務,它是一個可靠且輕量級的選擇。
5. 使用 JSON-P(Java JSON 處理 API)
JSON-P(Java API for JSON Processing)是用於 JSON 處理的標準化 Java API。它允許手動解析 JSON,並完全控制類型轉換,這使得解析非常精確,但略顯冗長。
要使用 JSON-P,我們需要將API和實作的依賴項新增到我們的pom.xml
檔案中:
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>org.eclipse.parsson</groupId>
<artifactId>parsson</artifactId>
<version>1.1.5</version>
</dependency>
使用 JSON-P,我們可以將 JSON 解析為映射,同時明確處理每個值的類型:
public class JsonPDeserializer {
public Map<String, Object> deserializeToMapUsingJSONP(String jsonString) {
try (JsonReader reader = Json.createReader(new StringReader(jsonString))) {
JsonObject jsonObject = reader.readObject();
return convertJsonToMap(jsonObject);
} catch (Exception e) {
throw new RuntimeException("JSON-P parsing failed: " + e.getMessage(), e);
}
}
private Map<String, Object> convertJsonToMap(JsonObject jsonObject) {
Map<String, Object> result = new HashMap<>();
for (Map.Entry<String, JsonValue> entry : jsonObject.entrySet()) {
String key = entry.getKey();
JsonValue value = entry.getValue();
result.put(key, convertJsonValue(value));
}
return result;
}
private Object convertJsonValue(JsonValue jsonValue) {
switch (jsonValue.getValueType()) {
case STRING:
return ((JsonString) jsonValue).getString();
case NUMBER:
jakarta.json.JsonNumber num = (JsonNumber) jsonValue;
return num.isIntegral() ? num.longValue() : num.doubleValue();
case TRUE:
return true;
case FALSE:
return false;
case NULL:
return null;
case ARRAY:
return convertJsonArray((JsonArray) jsonValue);
case OBJECT:
return convertJsonToMap((JsonObject) jsonValue);
default:
return jsonValue.toString();
}
}
private List<Object> convertJsonArray(JsonArray jsonArray) {
List<Object> list = new ArrayList<>();
for (JsonValue value : jsonArray) {
list.add(convertJsonValue(value));
}
return list;
}
}
在此範例中,我們使用JsonReader
將 JSON 字串解析為JsonObject
。然後,我們使用convertJsonValue()
手動轉換每個值,該函數可以明確處理字串、數字、布林值、陣列、巢狀物件和空值。
Arrays
被轉換為List<Object>
,以便在 Java 中更輕鬆地處理。這種方法可以完全控制如何將每個 JSON 值轉換為對應的 Java 類型。
我們可以用一個範例 JSON 來驗證實作:
Map<String, Object> result = deserializeToMapUsingJSONP(json);
assertEquals("John", result.get("name"));
assertEquals(30.0, result.get("age"));
assertEquals(true, result.get("isActive"));
assertEquals(50000.75, result.get("salary"));
assertTrue(result.get("hobbies") instanceof List);
assertTrue(result.get("address") instanceof Map);
JSON-P 仔細解析數字;整數和小數被轉換為Double
,確保精確的型別處理。
使用 JSON-P 的主要優勢在於它能夠精確控制類型轉換,因此在需要精確處理數字、布林值和巢狀結構時非常理想。缺點是程式碼更加冗長,我們必須手動處理所有 JSON 類型。
6. 總結
以下是 Jackson、Gson、org.json 和 JSON-P 的比較摘要表:
圖書館 | 依賴關係 | 類型處理 | 用例 |
---|---|---|---|
傑克森 | 是的 | 出色的類型保存(整數、長整型、雙精度型、布林型、列表、嵌套物件) | 生產應用程式、複雜的 JSON 結構、性能關鍵項目 |
格森 | 是的 | 數字預設為 Double;處理布林值、字串、列表和嵌套對象 | 簡單的項目,輕量級的 JSON 解析,快速設定 |
org.json | 是(或有時包含) | 保留整數和雙精度值;布林值、字串、JSONArray 轉換為列表 | 小型項目,簡單的 JSON 解析,最小依賴 |
JSON-P | 不,這是 Java 標準的一部分 | 數字預設為 Double;明確處理布林值、字串、列表、巢狀物件和空值 | 當需要精確的類型控制時,需要嚴格的解析和標準的Java專案。 |
7. 結論
在本文中,我們探討了多種將 JSON 資料反序列化為Map<String, Object>
並保留正確資料類型的方法。 Jackson 為複雜的 JSON 提供了最全面的解決方案,而 Gson、org.json 和 JSON-P 則根據我們的專案需求提供了更輕量級或更可控的替代方案。
與往常一樣,原始碼可在 GitHub 上取得。