1. 程式人生 > 程式設計 >複雜JSON字串轉換為Java巢狀物件的實現

複雜JSON字串轉換為Java巢狀物件的實現

目錄
  • 背景
  • 方法
    • 預備工作
    • 構建物件模型
    • 使用jackson 庫解析
    • 使用GSON解析
    • 不含列表的巢狀物件

背景

實際開發中,常常需要將比較複雜的 ON 字串轉換為對應的 物件。這裡記錄下解決方案。

如下所示,是入侵事件檢測得到的 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,"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,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")
   xmGelYQHL
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型別轉化
    SimpleDateFohttp://www.cppcns.comrmat fmt www.cppcns.com= new SimpleDatewww.cppcns.comFormat("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\",\"userId\":1476}}";
    BookInfo bookInfo = JsonUtil.toObject(json,BookInfo.class);
    Assert.assertNotNull(bookInfo);
}
@Test
public void testParseSimpleNestedJson() {
    String json = "{\"goods\":{\"desc\":\"2箱*250g\",\"userId\":1476}}";
    BookInfo bookInfo = GsonUtil.fromJson(json,BookInfo.class);
    Assert.assertNotNull(bookInfo);
}

讀者可以自行解析出 BookInfo 的物件模型。

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援我們。