首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 反射實現 JavaBean 轉 JSON:零依賴手寫序列化器

     編輯:Java 相關知識

為什麼要手寫反射序列化?

  • 理解 JSON 序列化底層原理(Jackson/Gson 內部同樣依賴反射)
  • 極簡環境無法引入第三方 JAR 時的備選方案
  • 特殊字段過濾、命名策略、敏感字段脫敏等定製化需求
  • 面試高頻考點:反射 + 字符串構建 + 泛型擦除

核心實現

import java.lang.reflect.*;
import java.util.*;

public class ReflectionJsonSerializer {

    // 入口:List 序列化
    public static String serializeList(List<?> list) {
        if (list == null) return "[]";
        StringBuilder sb = new StringBuilder("[");
        String sep = "";
        for (Object obj : list) {
            sb.append(sep).append(serializeObject(obj));
            sep = ",";
        }
        return sb.append("]").toString();
    }

    // 核心:單對象序列化
    public static String serializeObject(Object obj) {
        if (obj == null) return "null";
        Class<?> clazz = obj.getClass();
        StringBuilder sb = new StringBuilder("{");
        String sep = "";

        // 獲取所有聲明字段(含 private)
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            sb.append(sep);
            sep = ",";

            // 字段名
            sb.append('"').append(field.getName()).append("\":");

            // 讀取值:優先調用 getter,否則強制訪問字段
            Object value = null;
            try {
                String getter = "get" + Character.toUpperCase(field.getName().charAt(0)) 
                                + field.getName().substring(1);
                Method method = clazz.getMethod(getter);
                value = method.invoke(obj);
            } catch (NoSuchMethodException e) {
                // 無 getter,嘗試直接讀取字段
                field.setAccessible(true);
                try { value = field.get(obj); } 
                catch (IllegalAccessException ignored) {}
            } catch (Exception ignored) {}

            // 值序列化
            appendValue(sb, value);
        }
        return sb.append("}").toString();
    }

    // 值類型分發
    private static void appendValue(StringBuilder sb, Object value) {
        if (value == null) {
            sb.append("null");
        } else if (value instanceof String) {
            sb.append('"').append(escapeJson((String) value)).append('"');
        } else if (value instanceof Number || value instanceof Boolean) {
            sb.append(value.toString());
        } else if (value instanceof Collection) {
            sb.append(serializeList((List<?>) value)); // 簡化:僅處理 List
        } else if (value.getClass().isArray()) {
            sb.append(serializeArray(value));
        } else {
            // 嵌套對象遞歸
            sb.append(serializeObject(value));
        }
    }

    private static String serializeArray(Object array) {
        int len = Array.getLength(array);
        StringBuilder sb = new StringBuilder("[");
        for (int i = 0; i < len; i++) {
            if (i > 0) sb.append(",");
            appendValue(sb, Array.get(array, i));
        }
        return sb.append("]").toString();
    }

    // 簡易 JSON 轉義
    private static String escapeJson(String s) {
        return s.replace("\\", "\\\\")
                .replace("\"", "\\\"")
                .replace("\n", "\\n")
                .replace("\r", "\\r")
                .replace("\t", "\\t");
    }
}

測試用例

public class User {
    private String username;
    private String sex;
    private int age;
    // getter/setter 省略
}

public static void main(String[] args) {
    List<User> users = Arrays.asList(
        new User("張三", "男", 25),
        new User("李四", "女", 28)
    );
    String json = ReflectionJsonSerializer.serializeList(users);
    System.out.println(json);
    // 輸出: [{"username":"張三","sex":"男","age":25},{"username":"李四","sex":"女","age":28}]
}

局限性與生產級增強方向

缺口增強方案
循環引用導致 StackOverflow引入 IdentityHashMap<Object, Boolean> visited 記錄已序列化對象
日期/時間類型無標準格式檢測 java.time.* / Date,統一輸出 ISO-8601
字段命名策略固定支援 @JsonProperty、蛇形命名、駝峰轉換配置
性能:反射每次解析 Method/Field緩存 Class -> Field[]/Method[] 元數據,或用 ASM 字節碼生成
無註解支援 (@JsonIgnore 等)解析字段/方法註解,動態跳過
輸出無格式化加入 prettyPrint 參數,控制縮進換行

總結

手寫反射 JSON 序列化器,核心在於 字段發現 → Getter/字段讀取 → 類型分發遞歸 → 字符串構建 四步。雖功能不及 Jackson/Gson 完備,但代碼量 < 100 行、零依賴、邏輯透明,是理解序列化原理、應對受限環境、實現定製化輸出的絕佳起點。生產環境請直接使用成熟庫,並在性能敏感處開啟 Afterburner (Jackson) 或預編譯 (Gson)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved