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保持連線,而不會在讀取一次之後就關閉,所以需要在初始化的時候多加入一些設定。具體可以參照
//初始化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;
}