1. 程式人生 > >Java解析json——Jackson

Java解析json——Jackson

在Java下,想要解析json檔案,已經有了現成的Jackson框架

有幾個關於Jackson的基礎例子,或者也可以看本文最後的附錄。看過之後也就大致瞭解Jackson的基本用法了。至少Java物件和json的直接轉化還是比較簡單的。

在這裡,我主要記錄一下自己使用readTree從伺服器接收json並解析的過程。

需求

寫一個客戶端程式,客戶端從伺服器接收json,並解析為Java自己的物件,進行處理。客戶端與伺服器之間的通訊使用的是最基本的Socket通訊。

難點

  • 需要從InputStream,流式讀取json。
  • 讀取到的json可能是多種Java物件。

在我的需求中,伺服器傳送來的json可能會對應Java中的多種任務物件:RebootTask、UpdateTask、TicketResTask。而他們都有一個共同的父類ServerTask。在json中,使用了一個“type”欄位標識他們分別是哪一種任務型別。

策略

不能直接簡單的像附錄一樣讀取並轉換json,需要對json的type欄位進行判斷,然後才能根據type進行轉換。因此,需要解析json的節點。而JsonNode可以解決我們這方面的需求。

查官網,在ObjectMapper中有readTree方法,可以從InputStream中讀取資料。其返回的形式恰好為JsonNode,正好可以解決上面的兩個難題。

實現

首先,我們需要初始化一個ObjectMapper,用以接收json。如果沒有什麼特殊需求,ObjectMapper可以簡單建立ObjectMapper mapper = new ObjectMapper();,不過在這裡我需要保證Socket保持連線,而不會在讀取一次之後就關閉,所以需要在初始化的時候多加入一些設定。具體可以參照

JsonFactory,設定其Feature。類似的還有JsonParser的FeatureJsonGenerator的Feature。有更多需求的話再去看吧。

//初始化jackson,保持socket始終連通,不會在讀寫一次後關閉socket連線
JsonFactory jsonFactory = new JsonFactory();
jsonFactory.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE);
ObjectMapper mapper = new ObjectMapper(jsonFactory);

//讀取json
JsonNode rootNode = mapper.readTree
(cinemaClient.getInputStream());

拿到JsonNode物件之後,就可以使用其public abstract JsonNode path(String fieldName)方法,獲取type欄位的內容。

然後根據public <T> T treeToValue(TreeNode n, Class<T> valueType)方法,將JsonNode(implements TreeNode)傳入,再傳入需要轉換成的Java物件的class即可。

具體程式碼中,我們是將json(陣列,包含多個物件)轉換為多個物件,然後存到ArrayList中的,ArrayList的型別為所有任務的父類ServerTask:

    /**
     * 根據Server發來的json檔案解析ServerTask,生成ServerTask物件
     * @param mapper
     * @param rootNode
     * @return
     */
    private ArrayList<ServerTask> parseTask(ObjectMapper mapper, JsonNode rootNode) {
        try {
            //獲取taskList列表
            JsonNode taskList = rootNode.path(Constant.CONST_TASKLIST);

            ArrayList<ServerTask> serverTasks = new ArrayList<ServerTask>();
            if(taskList.isArray()) {
                //遍歷taskList,獲取type欄位,並根據type將任務解析為相應的物件
                for(JsonNode jNode : taskList) {
                //兩句核心程式碼
                    String type = jNode.path(Constant.CONST_TYPE).asText();
                    serverTasks.add(mapper.treeToValue(jNode, getTaskClass(type)));
                }
            }
            return serverTasks;
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("Server傳送json有誤:任務列表不是Array。");
            System.out.println("Server sending json ERROR: taskList is not an Array.");
            return null;
        }
    }

在上面的程式碼中,為了方便傳入Java物件的class進行轉換,我們可以先寫一個根據type欄位的值返回物件型別的小函式:

    /**
     * 返回ServerTask子類的類型別
     * @param type
     * @return
     */
    private Class<? extends ServerTask> getTaskClass(String type) {
        switch (type) {
            case Constant.CONST_TICKETRES :
                return TicketResTask.class;
            case Constant.CONST_UPDATE :
                return UpdateTask.class;
            case Constant.CONST_REBOOT :
                return RebootTask.class;
            default:
                System.out.println("json任務型別出錯!");
                System.out.println("Type of mission in json ERROR!");
                return null;
        }
    }

至此,解析完畢。就可以完全由Java去處理ArrayList<ServerTask>物件了。

附:基礎的Java物件與json轉換小示例

public static <T> T fromJson(Object object, TypeReference<T> clazz) {
//        ObjectMapper mapper = new ObjectMapper();
        try {
            if(object instanceof String){
                return mapper.readValue((String) object, clazz);
            }
            else if(object instanceof File){
                return mapper.readValue((File) object, clazz);
            }
            else return null;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

然後就可以將json轉換為TicketResTask型別的Java物件了——
讀取並轉換單個:

TicketResTask ticketTask = fromJson(new File("CinemaClient/resource/TicketResTask.json"), new TypeReference<TicketResTask>() {});

讀取並轉換多個:

ArrayList<TicketResTask> ticketTasks = fromJson(new File("CinemaClient/resource/TicketResTasks.json"), new TypeReference<ArrayList<TicketResTask>>() {});
    public static boolean toJsonFile(File file, Object object) {
//        ObjectMapper mapper = new ObjectMapper();
        try {
            mapper.writeValue(file, object);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    public static String toJson(Object object) {

        try {
//            ObjectMapper mapper = new ObjectMapper();
            return mapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }