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 類型 — String 用於 name,Integer 用於 age,Boolean 用於 isActive,Double 用於 salary,以及 ArrayList 用於 hobbies。
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 反序列化為鍵為字符串,值為任何類型的映射。
讓我們用一個示例 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. Using JSON-P (Java JSON Processing API)
JSON-P (Java API for JSON Processing) 是一種標準化的 Java API,用於 JSON 處理。它允許手動解析 JSON,並對類型轉換具有完全控制權,這使得它非常精確,但略顯冗長。
要使用 JSON-P,我們需要為 API 和一個 實現 添加依賴項到我們的 pom.xml 文件中:
jakarta.json
jakarta.json-api
2.1.3
org.eclipse.parsson
parsson
1.1.5
使用 JSON-P,我們可以將 JSON 解析為 map,同時顯式處理每個值的類型:
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(),該方法處理字符串、數字、布爾值、數組、嵌套對象和 null 值。
數組 被轉換為 List<Object> 以便在 Java 中更輕鬆地處理。這種方法提供了對每個 JSON 值如何轉換為相應的 Java 類型完全的控制。
我們可以使用以下示例進行驗證:
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 的總結表:
| 庫 (庫) | 依賴 (依賴) | 類型處理 (類型處理) | 使用場景 (使用場景) |
|---|---|---|---|
| Jackson | 是 (是) | 極佳的類型保留 (Integer, Long, Double, Boolean, List, 嵌套對象 (嵌套對象)) | 生產應用 (應用), 複雜 JSON 結構 (結構), 性能關鍵項目 (項目) |
| Gson | 是 (是) | 數字 (數字) 默認 (默認) 為 Double; 處理 Boolean, String, List, 和 嵌套對象 (嵌套對象) | 簡單項目 (項目), 輕量級 JSON 解析 (解析), 快速設置 (設置) |
| org.json | 是 (是) (或有時包含在其中) | Integer 和 Double 保留 (保留), Boolean, String, JSONArray 轉換為 List | 小型項目 (項目), 簡單 JSON 解析 (解析), 最小依賴 (依賴) |
| JSON-P | 否 (否), Java 標準的一部分 (的一部分) | Numbers 默認 (默認) 為 Double; 處理 Boolean, String, List, 嵌套對象 (嵌套對象), 和 nulls 顯式 (顯式) | 當需要精確的類型控制 (控制) 時, 需要嚴格的解析和標準 Java 項目 (項目)。 |
7. 結論
在本文中,我們探討了多種將 JSON 數據反序列化為 Map<String, Object> 的方法,同時保持正確的數據類型。 Jackson 為複雜的 JSON 提供最全面的解決方案,而 Gson、org.json 和 JSON-P 則根據我們的項目要求提供更輕量級或更精細的替代方案。