• 复杂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
    @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;
    }
    
    
    

    这里有两个注意点:

    • 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> 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 入参的方法:

    
    @Test
    public 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);
    }
    
    @Test
    public 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 的对象模型。


  • 相关阅读:
    [bug] C:warning: implicit declaration of function ‘getline’
    [Java] 内部类
    [架构] 数据库技术选型
    [bug] Maven修改pom文件后jdk版本回退
    [物联网] 电气 & 工控
    [bug] mysql 忘记密码
    [物联网] 电路分析
    机器学习sklearn(二十五): 模型评估(五)量化预测的质量(二)分类指标
    机器学习sklearn(二十四): 模型评估(四)量化预测的质量(一)scoring 参数: 定义模型评估规则
    机器学习sklearn(二十三): 模型评估(三)交叉验证:评估估算器的表现(三) 交叉验证迭代器
  • 原文地址:https://www.cnblogs.com/lovesqcc/p/14798434.html
Copyright © 2020-2023  润新知