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 是 Google 創建的另一個流行的 Java JSON 庫。它輕量級、易於使用,並且非常適合簡單的序列化和反序列化任務。
要使用 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. 使用 JSON-P (Java JSON 處理 API)
JSON-P (Java JSON 處理 API) 是一個標準化的 Java API,用於 JSON 處理。它允許手動解析 JSON,並對類型轉換擁有完全控制,這使得它非常精確,但同時也略顯冗長。
要使用 JSON-P,我們需要在我們的 pom.xml 文件中添加對 API 和實現的依賴:
<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() 方法,該方法顯式處理字符串、數字、布爾值、數組、嵌套對象和 null 值。
數組 被轉換為 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 的總結表:
| 庫 | 依賴 | 類型處理 | 使用場景 |
|---|---|---|---|
| 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 標準的一部分 | 數字默認轉換為 Double; 處理 Boolean, String, List, 嵌套對象,以及明確的 null | 當需要精確的類型控制時,需要嚴格的解析和標準 Java 項目。 |
7. 結論
在本文中,我們探討了多種將 JSON 數據反序列化為 <em Map<String, Object></em>> 的方法,同時保持正確的數據類型。Jackson 提供了最全面的解決方案,適用於複雜的 JSON 數據,而 Gson、org.json 和 JSON-P 則作為更輕量級或更受控的替代方案,具體取決於項目的需求。