1

I have a list of JSON urls in the following format:

{
  "level": 1,
  "secondLevel": {
    "level": 2,
    "thirdLevel": [
      {
        "level": 3,
        "fourthLevel": {
          "level": 4,
          "fifthLevel": [
            {
              "type": "URLS",
              "listOfimageURLs": [
                {
                  "urlNo": "Num1",
                  "data": "num1Data"
                },
                {
                  "urlNo": "Num2",
                  "data": "num2Data"
                },
                {
                  "urlNo": "Num3",
                  "data": "num3Data"
                }
              ]
            }
          ]
        }
      }
    ]
  }
}

Initially I had something like this, gave me a List of Urls with everything in the List.

@Data
@NoArgsConstructor
public class URLDetail {
  private String type;
  private List<Urls> urlData;
}

Now I have the following Java POJO I'm trying to deserialise to, with the value num2Data set in the field urlData

@Data
@NoArgsConstructor
public class URLDetail {
  private String type;
  private String urlData;
}

I'm trying to figure out how I can conditionally loop through the JSON list - url and for any urlNo equal to "Num2" deserialise the associated "data" field value (in this case "num2Data") to the URLDetail POJO field urlData?

I have the usual

mapper.readValue(json, URLDetail.class);

If I remove urlData the type field is correctly set, but completely un-sure about applying logic to parse the urls list in JSON.

Do I need to create some kind of custom deserialiser?

Jay Gehlot
  • 61
  • 1
  • 4
  • Possible duplicate of https://stackoverflow.com/questions/44042854/how-to-conditionally-deserialize-to-a-pojo-field-using-jackson – aksappy Nov 16 '19 at 00:29
  • I saw the the above link before I raised the question. Doesn't quite do what I wanted. I'd like to keep one type, one class. Not two. – Jay Gehlot Nov 16 '19 at 10:03
  • @CássioMazzochiMolin any thoughts, just looking at something very similar you've answered here https://stackoverflow.com/questions/38840659/conditional-field-requirement-based-on-another-field-value-in-jackson – Jay Gehlot Nov 16 '19 at 11:54

1 Answers1

1

In a case like this it is a good option to use JsonPath together with Jackson. JsonPath allows us to find required node in JSON payload and with help of Jackson we can easily map it to POJO.

Simple example:

import com.fasterxml.jackson.annotation.JsonProperty;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.TypeRef;
import com.jayway.jsonpath.spi.json.JacksonJsonProvider;
import com.jayway.jsonpath.spi.json.JsonProvider;
import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
import com.jayway.jsonpath.spi.mapper.MappingProvider;

import java.io.File;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;

public class JsonPathApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        // Initialize JsonPath to use Jackson
        Configuration.setDefaults(new JacksonDefaultConfiguration());

        String jsonPath = String.format("$..listOfimageURLs[?(@.urlNo == \"%s\")]", "Num2");
        TypeRef<List<URLDetail>> urlDetailsType = new TypeRef<List<URLDetail>>() {
        };
        List<URLDetail> details = JsonPath.parse(jsonFile).read(jsonPath, urlDetailsType);

        details.forEach(System.out::println);
    }
}

class URLDetail {

    @JsonProperty("urlNo")
    private String type;

    @JsonProperty("data")
    private String urlData;

    // getters, setters, toString or lombok annotations
}

class JacksonDefaultConfiguration implements Configuration.Defaults {

    private final JsonProvider jsonProvider = new JacksonJsonProvider();
    private final MappingProvider mappingProvider = new JacksonMappingProvider();

    @Override
    public JsonProvider jsonProvider() {
        return jsonProvider;
    }

    @Override
    public Set<Option> options() {
        return EnumSet.noneOf(Option.class);
    }

    @Override
    public MappingProvider mappingProvider() {
        return mappingProvider;
    }
}

Above code prints:

URLDetail{type='Num2', urlData='num2Data'}
Michał Ziober
  • 37,175
  • 18
  • 99
  • 146