javajsonmicroservicesjackson-databindobjectmapper

How to unmarshall a string constant object from JSON to as a Java Object?


I have the following JSON:

{
  "code":1000,
  "message":"Success",
  "data":{
    "results":[
      {
        "lineId":"C4000LG2020253739",
        "lineDiagnostics":{
          "C4000LG2020253739":{
            "ap":{
              "broadbanddsthroughput":{
                "broadbandDsThroughput":[
                  {
                    "average":104830,
                    "std":0,
                    "detection":0,
                    "numErrorFreeSamples":1,
                    "sampleMaxPercentile":107673,
                    "latestSampleTimestamp":1698160893000,
                    "sampleMax":107673,
                    "url":"http://blah-blah.com",
                    "videoQuality":7,
                    "serviceDetection":-1,
                    "percentile":[
                      104830
                    ],
                    "latestSample":104830,
                    "primaryIp":"205.171.3.100",
                    "speedTestTrafficMB":56.9910995
                  }
                ]
              },
              "broadbandusthroughput":{
                "broadbandUsThroughput":[
                  {
                    "average":37828,
                    "std":0,
                    "numErrorFreeSamples":1,
                    "sampleMaxPercentile":38393,
                    "latestSampleTimestamp":1698160893000,
                    "sampleMax":38393,
                    "url":"http://blah-blah.com",
                    "serviceDetection":-1,
                    "percentile":[
                      37828
                    ],
                    "latestSample":37828,
                    "primaryIp":"205.171.3.100",
                    "speedTestTrafficMB":21.8231345
                  }
                ]
              }
            },
            "interface":{
              
            },
            "station":{
              
            },
            "multiWan":{
              
            }
          }
        },
        "analysisDay":20231024
      }
    ]
  }
}

Under the lineDiagnostics, we have the "C4000LG2020253739": {...} and I need the contents from inside this object for my further calculations, could anyone please let me know how can this be achieved?

I am using Jackson to map the JSON values to a Java class.

Note: This particular string value "C4000LG2020253739":{...} will change with every microservice call(and not the contents inside this{...}), so I am facing problem with creating a general purpose Java class for this JSON structure.

I am using Jackson to map the JSON values to a Java class:

public class Class1{
    //historical speed response structure : map from json response
    String code;
    String message;

    @JsonProperty("data")
    DataSpeeds dataSpeeds;
...}

public class DataSpeeds {
    @JsonProperty("results")
    List<Results> results;

    @JsonProperty("lineId")
    String lineId;
...}

public class Results {

    String lineId;

    @JsonProperty("lineDiagnostics")
    LineDiagnostics lineDiagnostics;
...}

public class LineDiagnostics {
    @JsonProperty("ap")
    Ap ap;
...}

Inside the LineDiagnostics object, I need to map the "C4000LG2020253739":{...} from JSON, how can this be done? any pointers?


Solution

  • Option 1

    At least you can deserialize json to Map and retrieve required data from it.

    public static Map<String, Object> getLineDiagnosticByLineId(File file, String lineId) throws IOException {
        TypeReference<HashMap<String, Object>> typeRef = new TypeReference<>() {
        };
    
        Map<String, Object> map = new ObjectMapper().readValue(file, typeRef);
        Map<String, Object> data = (Map<String, Object>) map.getOrDefault("data", Map.of());
        List<Object> results = (List<Object>) data.getOrDefault("results", List.of());
    
        return results.stream()
                .map(result -> (Map<String, Object>) result)
                .filter(result -> lineId.equals(result.get("lineId")))
                .map(result -> result.getOrDefault("lineDiagnostics", List.of()))
                .map(lineDiagnostics -> (Map<String, Object>) lineDiagnostics)
                .findFirst().orElse(Map.of());
    }
    

    Option 2

    But I recommend you to build a model and use it instead of Map.

    Simple model could look like this:

    @Getter
    @Setter
    public class DataModel {
    
        private Integer code;
        private String message;
        private Data data;
    
        @Getter
        @Setter
        public static class Data {
    
            private List<Result> results;
    
        }
    
        @Getter
        @Setter
        public static class Result {
    
            private String lineId;
            private Map<String, Object> lineDiagnostics;
            private Integer analysisDay;
    
        }
    
    }
    

    And your business code like:

    public static Map<String, Object> getLineDiagnosticByLineId(File file, String lineId) throws IOException {
        DataModel dataModel = new ObjectMapper().readValue(file, DataModel.class);
        return dataModel.getData().getResults().stream()
                .filter(result -> lineId.equals(result.getLineId()))
                .map(DataModel.Result::getLineDiagnostics)
                .findFirst().orElse(Map.of());
    }
    

    Option 3

    And finally. You can solve your problem with mup Map<String, Object> inside LineDiagnostics and define setter with @JsonAnySetter annotation.

    @Getter
    @Setter
    public class DataModel {
    
        private Integer code;
        private String message;
        private Data data;
    
        @Getter
        @Setter
        public static class Data {
    
            private List<Result> results;
    
        }
    
        @Getter
        @Setter
        public static class Result {
    
            private String lineId;
            private LineDiagnostics lineDiagnostics;
            private Integer analysisDay;
    
        }
    
        @Getter
        @Setter
        public static class LineDiagnostics {
    
            private Map<String, LineDiagnostic> lineDiagnostics = new HashMap<>();
    
            @JsonAnySetter
            public void setLineDiagnostic(String key, LineDiagnostic value) {
                lineDiagnostics.put(key, value);
            }
    
        }
    
        @Getter
        @Setter
        public static class LineDiagnostic {
    
            private Ap ap;
            @JsonProperty("interface")
            private Object anInterface;
            private Object station;
            private Object multiWan;
    
        }
    
        @Getter
        @Setter
        public static class Ap {
    
            @JsonProperty("broadbanddsthroughput")
            private BroadbandDsThroughput broadbandDsThroughput;
            @JsonProperty("broadbandusthroughput")
            private BroadbandUsThroughput broadbandUsThroughput;
    
        }
    
        @Getter
        @Setter
        public static class BroadbandDsThroughput {
    
            private List<BroadbandThroughput> broadbandDsThroughput;
    
        }
    
        @Getter
        @Setter
        public static class BroadbandUsThroughput {
    
            private List<BroadbandThroughput> broadbandUsThroughput;
    
        }
    
        @Getter
        @Setter
        public static class BroadbandThroughput {
    
            private Double average;
            private Double std;
            private Double detection;
            private Integer numErrorFreeSamples;
            private Integer sampleMaxPercentile;
            private Long latestSampleTimestamp;
            private Integer sampleMax;
            private String url;
            private Integer videoQuality;
            private Integer serviceDetection;
            private List<Integer> percentile;
            private Integer latestSample;
            private String primaryIp;
            @JsonProperty("speedTestTrafficMB")
            private Double speedTestTrafficMb;
    
        }
    
    }
    

    And your final result should look like this:

    public static DataModel.LineDiagnostics getLineDiagnosticByLineId(File file, String lineId) throws IOException {
        DataModel dataModel = new ObjectMapper().readValue(file, DataModel.class);
        
        return dataModel.getData().getResults().stream()
                .filter(result -> lineId.equals(result.getLineId()))
                .map(DataModel.Result::getLineDiagnostics)
                .findFirst().orElse(null);
    }