Json 相關

Posted by Adam on August 24, 2022
### 類別轉字串 (Object => String) ```java import com.fasterxml.jackson.databind.ObjectMapper; public class Example { public static void main(String[] args) { // 創建 ObjectMapper 實例 ObjectMapper objectMapper = new ObjectMapper(); // 創建一個 Java 對象 Person person = new Person("John", "Doe", 30); try { // 將 Java 對象轉換為 JSON 字串 String json = objectMapper.writeValueAsString(person); System.out.println(json); } catch (Exception e) { e.printStackTrace(); } } } @Data @AllArgsConstructor class Person { private String firstName; private String lastName; private int age; } ``` ### 字串轉類別 (String => Object) ```java import com.fasterxml.jackson.databind.ObjectMapper; public class Main { public static void main(String[] args) throws Exception { String jsonString = "{\"name\":\"John\", \"age\":30}"; ObjectMapper objectMapper = new ObjectMapper(); MyClass myObject = objectMapper.readValue(jsonString, MyClass.class); System.out.println(myObject.getName()); System.out.println(myObject.getAge()); } } class MyClass { private String name; private int age; // getters and setters } ``` ### Map 轉換成對應的類別 (Map => Object) ```java ObjectMapper objectMapper = new ObjectMapper(); Map<String, Object> map = new HashMap<>(); map.put("name", "John"); map.put("age", 25); MyClass myClass = objectMapper.convertValue(map, MyClass.class); ``` ### Object 轉成 Map ```java import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Map; public class JacksonObjectToMapExample { public static void main(String[] args) { // 創建 ObjectMapper 實例 ObjectMapper objectMapper = new ObjectMapper(); // 創建您的自定義對象 YourCustomObject customObject = new YourCustomObject(); customObject.setProperty1("value1"); customObject.setProperty2(2); // 將對象轉換為 Map Map<String, Object> map = objectMapper.convertValue(customObject, Map.class); // 打印轉換後的 Map System.out.println("Map: " + map); } } ``` --- 使用 ObjectMapper 將物件轉換成 Json 格式時,欄位的順序會根據 Java 物件內部的屬性順序轉換成相對應的 Json Key,但是這個順序不是固定的,因為 Java 物件屬性在記憶體中的分配順序並不是固定的。 若需要控制 Json 輸出的順序,可以使用 @JsonPropertyOrder 或 @JsonPropertyIndex 來控制屬性的輸出順序,例如: ```java @JsonPropertyOrder({ "name", "age", "email" }) public class User { private String name; private int age; private String email; // 省略 getter、setter 方法 } ``` ```java public class User { @JsonProperty("name") private String fullName; @JsonProperty("email") private String emailAddress; // 其他屬性和方法... } ``` ### 物件 轉 MD5 ```java import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.digest.DigestUtils; public class MD5Utils { public static String calculateMD5(Object object) throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); String jsonString = objectMapper.writeValueAsString(object); return DigestUtils.md5Hex(jsonString); } } ``` --- `@JsonIgnore` 是 Jackson 库中用來控制 JSON 序列化和反序列化過程中屬性忽略的注解。以下是 `@JsonIgnore` 的一些常見用法: ### 1. 忽略序列化和反序列化: ```java public class MyClass { private String name; private int age; // Getter and setter methods @JsonIgnore public int getAge() { return age; } } ``` 在這個例子中,`@JsonIgnore` 注解應用在 `getAge` 方法上,表示在 JSON 序列化和反序列化過程中,`age` 屬性將被忽略。 ### 2. 忽略特定方向的序列化: ```java public class MyClass { private String name; private int age; // Getter methods @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) private int secretCode; // Getter and setter methods } ``` 在這個例子中,`@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)` 注解應用在 `secretCode` 屬性上,表示這個屬性只會在 JSON 反序列化時被使用,而在序列化時會被忽略。 ### 3. 在字段上應用 `@JsonIgnore`: ```java public class MyClass { @JsonIgnore private String sensitiveData; // Getter and setter methods } ``` 你也可以將 `@JsonIgnore` 直接應用在屬性字段上,而不是 getter 方法。 ### 4. 在嵌套對象上使用: ```java public class MyParentClass { private String parentProperty; private MyChildClass child; // Getter and setter methods } public class MyChildClass { private String childProperty; // Getter and setter methods @JsonIgnore public String getChildProperty() { return childProperty; } } ``` 在這個例子中,`@JsonIgnore` 注解應用在 `getChildProperty` 方法上,表示在 JSON 序列化和反序列化過程中,`childProperty` 將被忽略。 這僅是 `@JsonIgnore` 的一些用法範例,實際上還有其他一些使用方式,具體取決於你的需求。請注意,`@JsonIgnore` 主要用於控制 JSON 序列化和反序列化的行為。 ### @JsonIgnoreProperties 在 Spring 中,若 JSON 回覆中包含未知的屬性,可以通過配置來忽略這些屬性。可以使用 @JsonIgnoreProperties 注解在需要忽略未知屬性的類上,如下所示: ```java @JsonIgnoreProperties(ignoreUnknown = true) public class MyResponse { private String property1; private String property2; // 省略 getter 和 setter } ``` 這樣配置之後,當 JSON 回覆中含有未知屬性時,這些屬性將被忽略,不會映射到對應的屬性上。這樣可以確保應用程序在處理回覆時可以安全地忽略未知屬性,防止因為未知屬性導致錯誤的出現。 ### @JsonInclude @JsonInclude(JsonInclude.Include.NON_NULL) 的作用是告訴 Jackson 只序列化非空值的屬性,而忽略空值的屬性。 舉例來說,假設有以下的 Java 類別: ```java public class User { private String name; private Integer age; private String email; // constructors, getters, setters } ``` 如果我們對 User 物件應用 @JsonInclude(JsonInclude.Include.NON_NULL): ```java @JsonInclude(JsonInclude.Include.NON_NULL) public class User { private String name; private Integer age; private String email; // constructors, getters, setters } ``` 當序列化一個 User 物件時,只有 name、age 和 email 不是空值的屬性會被包含在序列化結果中,而空值的屬性會被忽略。這樣可以讓序列化後的 JSON 較為簡潔且清晰。 ### 常見設定 ```java @Bean public ObjectMapper anotherobjectMapper() { ObjectMapper mapper = new ObjectMapper(); // ========================================== // 1. 反序列化設定 (Deserialization) - JSON 轉 Java 物件 // ========================================== // 【最重要】當 JSON 包含 Java 類別中沒有的屬性時,不拋出異常,直接忽略 mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 當 JSON 的基本型別(如 int/long)傳入空字串 "" 時,將其解析為 null,避免型態轉換錯誤 mapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true); // 允許 JSON 欄位名稱不使用雙引號(例如 {name: "小明"}),提高對非標準 JSON 的容錯率 mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); // 允許 JSON 使用單引號(例如 {'name': '小明'}) mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); // ========================================== // 2. 序列化設定 (Serialization) - Java 物件 轉 JSON // ========================================== // 當 Java 物件是空物件(沒有任何屬性)時,不拋出異常,而是輸出空大括號 "{}" mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); // 格式化輸出的 JSON 字串(自動換行與縮排),讓 Log 或 API 輸出美觀易讀(生產環境依效能需求決定是否開啟) mapper.configure(SerializationFeature.INDENT_OUTPUT, true); // 解決 BigDecimal 轉 JSON 時變成科學記號的問題(例如 1E+2 轉回正常的 100) mapper.configure(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN, true); // ========================================== // 3. 日期與時間處理 (Date & Time) // ========================================== // 【最重要】禁用「將日期轉為時間戳記(Timestamp數字)」,改為輸出人類可讀的字串 mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); // 設定全局的舊版 Date 格式(例如:2026-06-06 14:30:00) mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); // 設定全局時區,避免伺服器在 AWS 或不同地區時出現 8 小時的時差(GMT+8 台北時間) mapper.setTimeZone(TimeZone.getTimeZone("GMT+8")); // ========================================== // 4. 空值過濾設定 (Null / Empty Filtering) // ========================================== // 全局排除 null 值的欄位:如果 Java 屬性為 null,則不顯示在輸出的 JSON 中 mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 【替代方案】如果想更嚴格(連空字串 ""、空 List [] 都不輸出),可以啟用下方這行: // mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); // ========================================== // 5. 命名策略設定 (Naming Strategy) - 依需求開啟 // ========================================== // 駝峰命名轉蛇形命名(例如 Java 的 userId 轉為 JSON 的 user_id)。若前後端規範需要再開啟。 // mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); // ========================================== // 6. ROOT 根節點封裝設定 (依特定 API 規範開啟) // ========================================== // 開啟輸出封裝:輸出的 JSON 最外層會多包一層「類別名稱」作為 Key // 輸出範例:{"User": {"name": "小明"}} // mapper.enable(SerializationFeature.WRAP_ROOT_VALUE); // 開啟輸入解包:解析 JSON 時,會先剝離最外層的「類別名稱」Key,再讀取內容 // mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE); return mapper; } ``` ### 保留 Json 字串的順序 ```java import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; public class JsonNodeOrderTest { @Test void testJsonNodePreservesOrder() throws IOException { // 1. 準備一個 JSON 字串,其中鍵的順序是 'c', 'a', 'b' String jsonString = "{\"c\":\"value3\",\"a\":\"value1\",\"b\":\"value2\"}"; // 2. 使用 ObjectMapper 將 JSON 字串解析為 JsonNode ObjectMapper objectMapper = new ObjectMapper(); JsonNode rootNode = objectMapper.readTree(jsonString); // 3. 遍歷 JsonNode 的鍵,並將它們添加到一個列表中 List<String> keysInOrder = new ArrayList<>(); Iterator<String> fieldNames = rootNode.fieldNames(); while (fieldNames.hasNext()) { keysInOrder.add(fieldNames.next()); } // 4. 準備一個預期的順序列表 List<String> expectedOrder = List.of("c", "a", "b"); // 5. 斷言實際的鍵順序列表與預期的一致 assertEquals(expectedOrder, keysInOrder, "JsonNode 應該保留 JSON 物件的鍵順序"); } } ```