复杂JSON字符串转换为Java嵌套对象的实现

目录

  • 背景
  • 方法
    • 预备工作
    • 构建对象模型
    • 使用jackson 库解析
    • 使用GSON解析
    • 不含列表的嵌套对象
【复杂JSON字符串转换为Java嵌套对象的实现】
背景 实际开发中,常常需要将比较复杂的 JSON 字符串转换为对应的 Java 对象。这里记录下解决方案。
如下所示,是入侵事件检测得到的 JSON 串:
[{"rule_id":"反弹shell","format_output":"进程 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"}}}]

方法
预备工作
把上述 json 串放在 src/test/resources 下,写一个文件读写程序来解析。 其实放在哪里不重要,重要的是拿到这个 JSON 串便于后续解析。
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()); }}


构建对象模型
首先,要根据这个 JSON 字符串解析出对应的数据模型 AgentDetectEventData。主要就是按照 JSON 串中的 key 的层次结构来建立。
@Getter@Setterpublic 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@Setterpublic class AgentDetectEventDetail {@SerializedName("process_events")@JsonProperty("process_events")private ProcessEvent processEvent; @SerializedName("proc_trees")@JsonProperty("proc_trees")private List procTree; @SerializedName("containers")@JsonProperty("containers")private Container container; @SerializedName("sockets")@JsonProperty("sockets")private Socket socket; }@Getter@Setterpublic 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@Setterpublic 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@Setterpublic 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@Setterpublic 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; }

这里有两个注意点:
  • JSON 字符串的字段命名是下划线形式,而 Java 对象的属性命名是驼峰式的,这里需要做一个字段名映射转换。 使用 Jackson 库来转换,是 @JsonProperty 注解; 使用 gson 库来转换,是 @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类型转化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过程中发生了异常*/public static T toObject(String json, Class cls) {if (json == null) {return null; }try {return MAPPER.readValue(json, cls); } catch (Exception e) {throw new RuntimeException(e.getCause()); }}public static 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 jsonToObject(String src, TypeReference 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 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 {@Testpublic void testParseJson() {String json = RWTool.readFromSource("/json.txt"); List ade = JsonUtil.jsonToObject(json, new TypeReference() {}); Assert.assertNotNull(ade); }@Testpublic void testParseJson2() {String json = RWTool.readFromSource("/json.txt"); List ade = JsonUtil.jsonToObject(json, List.class, AgentDetectEventData.class); Assert.assertNotNull(ade); }}

引入POM依赖为:
org.codehaus.jacksonjackson-mapper-asl1.9.4


使用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 fromJson(String json, Class cls) {return getGson().fromJson(json, cls); }public static T fromJson(String json, Type type) {return getGson().fromJson(json, type); }}

单测:
public class GsonUtilTest {@Testpublic void testParseJson() {String json = RWTool.readFromSource("/json.txt"); List ade = GsonUtil.fromJson(json, new TypeToken(){}.getType()); Assert.assertNotNull(ade); }}

引入 POM 为:
com.google.code.gsongson2.3.1


不含列表的嵌套对象
如果是不含列表的嵌套对象,则使用带 Class cls 入参的方法:
@Testpublic void testParseSimpleNestedJson() {String json = "{\"goods\":{\"desc\":\"2箱*250g\",\"goodsId\":8866,\"orderNo\":\"E20210522120237009258\",\"shopId\":659494,\"title\":\"认养一头牛\"},\"order\":{\"bookTime\":1621656157,\"codPay\":false,\"deliveryType\":\"express\",\"orderNo\":\"E20210522120237009258\",\"shopId\":659494,\"userId\":1476}}"; BookInfo bookInfo = JsonUtil.toObject(json, BookInfo.class); Assert.assertNotNull(bookInfo); }@Testpublic void testParseSimpleNestedJson() {String json = "{\"goods\":{\"desc\":\"2箱*250g\",\"goodsId\":8866,\"orderNo\":\"E20210522120237009258\",\"shopId\":659494,\"title\":\"认养一头牛\"},\"order\":{\"bookTime\":1621656157,\"codPay\":false,\"deliveryType\":\"express\",\"orderNo\":\"E20210522120237009258\",\"shopId\":659494,\"userId\":1476}}"; BookInfo bookInfo = GsonUtil.fromJson(json, BookInfo.class); Assert.assertNotNull(bookInfo); }

读者可以自行解析出 BookInfo 的对象模型。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

    推荐阅读