標準化/結構化 JSON 輸出

weixin_34082695發表於2018-11-19

在構建一個基於JSON的API的時候, 往往我們還會需要新增一些額外的欄位, 形成類似於 資料主體/額外資訊 結構的資料, 如:

{
    "mainContent": { // 資料主體
        "name": "小明",
        "age": 11
    },
    "external": { // 額外資訊
        "errorMsg": "OK",
        "errorCode": "0"
    }
}

方法1 建一個帶泛型的 pojo , 將資料塞進去, 然後JSON化輸出

這樣的類可以是:

public class BizDto<T> {
    
    public BizDto() {
        // 為了方便, 設定預設值
        External external = new External();
        external.setErrorCode("0");
        external.setErrorMsg("OK");
    }

    private T mainContent;
    private External external;

    public T getMainContent() {
        return mainContent;
    }

    public void setMainContent(T mainContent) {
        this.mainContent = mainContent;
    }

    public External getExternal() {
        return external;
    }

    public void setExternal(External external) {
        this.external = external;
    }

    public class External{
        private String errorMsg;
        private String errorCode;

        public String getErrorMsg() {
            return errorMsg;
        }

        public void setErrorMsg(String errorMsg) {
            this.errorMsg = errorMsg;
        }

        public String getErrorCode() {
            return errorCode;
        }

        public void setErrorCode(String errorCode) {
            this.errorCode = errorCode;
        }
    }
}

然後所有的 restfull api 都用這個 pojo 封裝一下再輸出

方法2 干預 spring json 序列化的過程

使用這個辦法的好處是 controller 方法的返回值不需要做任何改變, 可以直接輸出資料主體
先自定義HttpMessageConverter來實現自定義的 java轉換過程 (from java/ to java)
然後呢 通過覆蓋 spring 預設的 HttpMessageConverters, 或者將自己的 converter 加上去, 辦法是宣告一個 bean
@Bean
public HttpMessageConverters additionalConverters() {
return new HttpMessageConverters(new TheCustomConverter());
}

HttpMessageConverters 有3個構造方法

HttpMessageConverters(HttpMessageConverter<?>... additionalConverters)
HttpMessageConverters(Collection<HttpMessageConverter<?>> additionalConverters)
HttpMessageConverters(boolean addDefaultConverters, Collection<HttpMessageConverter<?>> converters)

前兩個是給spring 預設處理列表中新增一個新的 converter, 第三個是覆蓋掉預設的 converters, 只使用自己定義的

使用這個方法還需要考慮一種情況, 就是出錯了, 要怎麼輸出錯誤資訊到前端

思路是使用@ControllerAdvice + 自定義 runtime Exception
遇到錯誤, 直接 throw new CustomeRuntimeException("errorcode","errormsg", ex)
交給框架去處理

小尾巴:
既然 spring 預設帶了 jackson 作為 json 的序列化工具, 有沒有人想過直接使用這個工具來序列化java物件呢, 多數時候我們都是再用一個第三方jar包比如著名的 fastJSON. 但再新增一個第三方工具帶來的麻煩是 json 序列化時配置的不統一了
比如說 jackson 預設會將 Date 物件格式化成 utc 格式, 而fastJson會將日期輸出成 毫秒 時間戳
有一個全域性統一的配置, 可以避免在輸出時每次都控制一下格式化, 而且很容易遺漏
辦法就是, 注入 jackson 的ObjectMapper

    @Autowired
    private ObjectMapper objectMapper; // 注入
    // 輸出JSON字串
    objectMapper.writer().writeValueAsString(javaObj));
    // 將JSON字串轉成java bean;
    objectMapper.readValue(jsonStr,javaObj.class);

    Map<String, Object> map = new HashMap<String, Object>();
    // JSON string to Map
    map = objectMapper.readValue(json, new TypeReference<Map<String, String>>(){});


相關文章