復(fù)雜JSON字符串轉(zhuǎn)換為Java嵌套對象的實(shí)現(xiàn)
背景
實(shí)際開發(fā)中,常常需要將比較復(fù)雜的 JSON 字符串轉(zhuǎn)換為對應(yīng)的 Java 對象。這里記錄下解決方案。
如下所示,是入侵事件檢測得到的 JSON 串:
[{"rule_id":"反彈shell","format_output":"進(jìn)程 pname 反向連接到 %dest_ip%:%dest_port%","info":{"process_events":{"pid":21,"pname":"nginx","cmdline":"curl www.cfda.com","ppid":7,"ppname":"bash"},"proc_trees":[{"pid":21,"pname":"nginx","cmdline":"curl www.cfda.com","ppid":7,"ppname":"bash"}],"containers":{"container_id":"fef4636d8403871c2e56e06e51d609554564adbbf8284dd914a0f61130558bdf","container_name":"nginx","image_id":"4eb8f7c43909449dbad801c50d9dccc7dc86631e54f28b1a4b13575729065be8","status":"Running"},"sockets":{"src_ip":"127.0.0.1","src_port":"8080","type":"1","in_out":"0","dest_ip":"localhost","dest_port":"80"}}}]
方法
預(yù)備工作
把上述 json 串放在 src/test/resources 下,寫一個文件讀寫程序來解析。 其實(shí)放在哪里不重要,重要的是拿到這個 JSON 串便于后續(xù)解析。
public static String readFromSource(String filename) {
try {
InputStream is = RWTool.class.getResourceAsStream(filename);
byte[] bytes = new byte[4096];
int num = 0;
String json = "";
while((num=is.read(bytes))>0){
json=new String(bytes,0,num);
}
return json;
} catch (Exception ex) {
throw new RuntimeException(ex.getCause());
}
}
構(gòu)建對象模型
首先,要根據(jù)這個 JSON 字符串解析出對應(yīng)的數(shù)據(jù)模型 AgentDetectEventData。主要就是按照 JSON 串中的 key 的層次結(jié)構(gòu)來建立。
@Getter
@Setter
public class AgentDetectEventData {
@SerializedName("rule_id")
@JsonProperty("rule_id")
private String ruleId;
@SerializedName("format_output")
@JsonProperty("format_output")
private String formatOutput;
@SerializedName("info")
@JsonProperty("info")
private AgentDetectEventDetail info;
}
@Getter
@Setter
public class AgentDetectEventDetail {
@SerializedName("process_events")
@JsonProperty("process_events")
private ProcessEvent processEvent;
@SerializedName("proc_trees")
@JsonProperty("proc_trees")
private List<ProcessTree> procTree;
@SerializedName("containers")
@JsonProperty("containers")
private Container container;
@SerializedName("sockets")
@JsonProperty("sockets")
private Socket socket;
}
@Getter
@Setter
public class ProcessEvent {
@SerializedName("pid")
@JsonProperty("pid")
private String pid;
@SerializedName("pname")
@JsonProperty("pname")
private String pname;
@SerializedName("cmdline")
@JsonProperty("cmdline")
private String cmdline;
@SerializedName("ppid")
@JsonProperty("ppid")
private String ppid;
@SerializedName("ppname")
@JsonProperty("ppname")
private String ppname;
}
@Getter
@Setter
public class ProcessTree {
@SerializedName("pid")
@JsonProperty("pid")
private String pid;
@SerializedName("pname")
@JsonProperty("pname")
private String pname;
@SerializedName("cmdline")
@JsonProperty("cmdline")
private String cmdline;
@SerializedName("ppid")
@JsonProperty("ppid")
private String ppid;
@SerializedName("ppname")
@JsonProperty("ppname")
private String ppname;
}
@Getter
@Setter
public class Container {
@SerializedName("container_id")
@JsonProperty("container_id")
private String containerId;
@SerializedName("container_name")
@JsonProperty("container_name")
private String containerName;
@SerializedName("image_id")
@JsonProperty("image_id")
private String imageId;
@SerializedName("status")
@JsonProperty("status")
private String status;
}
@Getter
@Setter
public class Socket {
@SerializedName("src_ip")
@JsonProperty("src_ip")
private String srcIp;
@SerializedName("src_port")
@JsonProperty("src_port")
private String srcPort;
@SerializedName("type")
@JsonProperty("type")
private String type;
@SerializedName("in_out")
@JsonProperty("in_out")
private String inOut;
@SerializedName("dest_ip")
@JsonProperty("dest_ip")
private String destIp;
@SerializedName("dest_port")
@JsonProperty("dest_port")
private String destPort;
}
這里有兩個注意點(diǎn):
- JSON 字符串的字段命名是下劃線形式,而 Java 對象的屬性命名是駝峰式的,這里需要做一個字段名映射轉(zhuǎn)換。 使用 Jackson 庫來轉(zhuǎn)換,是 @JsonProperty 注解; 使用 gson 庫來轉(zhuǎn)換,是 @SerializedName 注解。
- 需要加 getter / setter 方法。
對象模型建立后,就成功了一大半。接下來,就是使用 json 庫來解析了。
使用jackson 庫解析
public class JsonUtil {
private static Logger logger = LoggerFactory.getLogger(JsonUtil.class);
private static final ObjectMapper MAPPER = new ObjectMapper();
static {
// 為保持對象版本兼容性,忽略未知的屬性
MAPPER.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 序列化的時候,跳過null值
MAPPER.getSerializationConfig().setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
// date類型轉(zhuǎn)化
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
MAPPER.setDateFormat(fmt);
}
/**
* 將一個json字符串解碼為java對象
*
* 注意:如果傳入的字符串為null,那么返回的對象也為null
*
* @param json json字符串
* @param cls 對象類型
* @return 解析后的java對象
* @throws RuntimeException 若解析json過程中發(fā)生了異常
*/
public static <T> T toObject(String json, Class<T> cls) {
if (json == null) {
return null;
}
try {
return MAPPER.readValue(json, cls);
} catch (Exception e) {
throw new RuntimeException(e.getCause());
}
}
public static <T> String objectToJson(T obj){
if(obj == null){
return null;
}
try {
return obj instanceof String ? (String) obj : MAPPER.writeValueAsString(obj);
} catch (Exception e) {
return null;
}
}
public static <T> T jsonToObject(String src, TypeReference<T> typeReference){
if(StringUtils.isEmpty(src) || typeReference == null){
return null;
}
try {
return (T)(typeReference.getType().equals(String.class) ? src : MAPPER.readValue(src, typeReference));
} catch (Exception e) {
logger.warn("Parse Json to Object error",e);
throw new RuntimeException(e.getCause());
}
}
public static <T> T jsonToObject(String src, Class<?> collectionClass,Class<?>... elementClasses){
JavaType javaType = MAPPER.getTypeFactory().constructParametricType(collectionClass,elementClasses);
try {
return MAPPER.readValue(src, javaType);
} catch (Exception e) {
logger.warn("Parse Json to Object error",e);
throw new RuntimeException(e.getCause());
}
}
}
單測:
public class JsonUtilTest {
@Test
public void testParseJson() {
String json = RWTool.readFromSource("/json.txt");
List<AgentDetectEventData> ade = JsonUtil.jsonToObject(json, new TypeReference<List<AgentDetectEventData>>() {});
Assert.assertNotNull(ade);
}
@Test
public void testParseJson2() {
String json = RWTool.readFromSource("/json.txt");
List<AgentDetectEventData> ade = JsonUtil.jsonToObject(json, List.class, AgentDetectEventData.class);
Assert.assertNotNull(ade);
}
}
引入POM依賴為:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.4</version>
</dependency>
使用GSON解析
public class GsonUtil {
static GsonBuilder gsonBuilder = null;
static {
gsonBuilder = new GsonBuilder();
gsonBuilder.setDateFormat("yyyy-MM-dd HH:mm:ss");
}
public static Gson getGson() {
return gsonBuilder.create();
}
public static <T> T fromJson(String json, Class<T> cls) {
return getGson().fromJson(json, cls);
}
public static <T> T fromJson(String json, Type type) {
return getGson().fromJson(json, type);
}
}
單測:
public class GsonUtilTest {
@Test
public void testParseJson() {
String json = RWTool.readFromSource("/json.txt");
List<AgentDetectEventData> ade = GsonUtil.fromJson(json, new TypeToken<List<AgentDetectEventData>>(){}.getType());
Assert.assertNotNull(ade);
}
}
引入 POM 為:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.3.1</version>
</dependency>
不含列表的嵌套對象
如果是不含列表的嵌套對象,則使用帶 Class cls 入?yún)⒌姆椒ǎ?/p>
@Test
public void testParseSimpleNestedJson() {
String json = "{\"goods\":{\"desc\":\"2箱*250g\",\"goodsId\":8866,\"orderNo\":\"E20210522120237009258\",\"shopId\":659494,\"title\":\"認(rèn)養(yǎng)一頭牛\"},\"order\":{\"bookTime\":1621656157,\"codPay\":false,\"deliveryType\":\"express\",\"orderNo\":\"E20210522120237009258\",\"shopId\":659494,\"userId\":1476}}";
BookInfo bookInfo = JsonUtil.toObject(json, BookInfo.class);
Assert.assertNotNull(bookInfo);
}
@Test
public void testParseSimpleNestedJson() {
String json = "{\"goods\":{\"desc\":\"2箱*250g\",\"goodsId\":8866,\"orderNo\":\"E20210522120237009258\",\"shopId\":659494,\"title\":\"認(rèn)養(yǎng)一頭牛\"},\"order\":{\"bookTime\":1621656157,\"codPay\":false,\"deliveryType\":\"express\",\"orderNo\":\"E20210522120237009258\",\"shopId\":659494,\"userId\":1476}}";
BookInfo bookInfo = GsonUtil.fromJson(json, BookInfo.class);
Assert.assertNotNull(bookInfo);
}
讀者可以自行解析出 BookInfo 的對象模型。
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
- Java中JSON字符串與java對象的互換實(shí)例詳解
- JSON的String字符串與Java的List列表對象的相互轉(zhuǎn)換
- Java實(shí)現(xiàn)Json字符串與Object對象相互轉(zhuǎn)換的方式總結(jié)
- Java中Json字符串直接轉(zhuǎn)換為對象的方法(包括多層List集合)
- FastJson對于JSON格式字符串、JSON對象及JavaBean之間的相互轉(zhuǎn)換操作
- json-lib將json格式的字符串,轉(zhuǎn)化為java對象的實(shí)例
- Json字符串轉(zhuǎn)Java對象和List代碼實(shí)例
- java中對象轉(zhuǎn)json字符串的幾種常用方式舉例
相關(guān)文章
JAVA錯誤類結(jié)果類和分頁結(jié)果類代碼詳解
這篇文章主要介紹了JAVA錯誤類結(jié)果類和分頁結(jié)果類代碼詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-02-02
SpringBoot中Bean拷貝及工具類封裝的實(shí)現(xiàn)
本文主要介紹了SpringBoot中Bean拷貝及工具類封裝的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05
Java輸出通過InetAddress獲得的IP地址數(shù)組詳細(xì)解析
由于byte被認(rèn)為是unsigned byte,所以最高位的1將會被解釋為符號位,另外Java中存儲是按照補(bǔ)碼存儲,所以1000 0111會被認(rèn)為是補(bǔ)碼形式,轉(zhuǎn)換成原碼便是1111 0001,轉(zhuǎn)換成十進(jìn)制數(shù)便是-1212013-09-09
springboot集成shiro權(quán)限管理簡單實(shí)現(xiàn)
這篇文章主要介紹了springboot集成shiro權(quán)限管理簡單實(shí)現(xiàn),文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-08-08
Mybatis-plus selectByMap條件查詢方式
這篇文章主要介紹了Mybatis-plus selectByMap條件查詢方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06
教你一步解決java.io.FileNotFoundException:找不到文件異常
這篇文章主要給大家介紹了關(guān)于如何一步解決java.io.FileNotFoundException:找不到文件異常的相關(guān)資料,文中通過圖文以及代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01

