Search code examples
javajacksonpojojsonschema2pojo

Map JSON with multiple unknown fields to Java POJO


I am trying to map the below JSON to java POJO. Below is the JSON.

{
  "customObject": {
    "value": {
      "config-1": {
        "configurationName": {
          "de": "Das ist ein Text",
          "en": "Hospitalization Cover"
        },
        "key": "hospitalization-cover",
        "mandatory": false,
        "type": "options",
        "options": {
          "1Lakh": {
            "label": "1 Lakh",
            "priceType": "fixed",
            "price": "200",
            "default": "true"
          },
          "2Lakh": {
            "label": "2 Lakh",
            "priceType": "fixed",
            "price": "400"
          },
          "3Lakh": {
            "label": "3 Lakh",
            "priceType": "fixed",
            "price": "450"
          },
          "4Lakh": {
            "label": "4 Lakh",
            "priceType": "fixed",
            "price": "500"
          },
          "5Lakh": {
            "label": "5 Lakh",
            "priceType": "fixed",
            "price": "550"
          },
          "6Lakh": {
            "label": "6 Lakh",
            "priceType": "fixed",
            "price": "650"
          },
          "7Lakh": {
            "label": "7 Lakh",
            "priceType": "fixed",
            "price": "780"
          }
        }
      },
      "config-2": {
        "configurationName": {
          "de": "Das ist ein Text",
          "en": "OPD Cover"
        },
        "key": "opd-cover",
        "mandatory": false,
        "type": "number",
        "options": {
          "OPDCover10k": {
            "label": "10,000",
            "priceType": "calculated",
            "priceFactor": 0.2,
            "default": "true"
          },
          "OPDCover15k": {
            "label": "15,000",
            "priceType": "calculated",
            "priceFactor": 0.27
          }
        }
      }
    }
  }
}

I have created below POJO to map it.

package com.edu.epam.graphQLSearch.custom.graphlQLqueries;

public class GqCustomObject {

    private GqConfigCustomPOValue customObject;

    public GqConfigCustomPOValue getCustomObject() {
        return customObject;
    }

    public void setCustomObject(GqConfigCustomPOValue customObject) {
        this.customObject = customObject;
    }
}

Above POJO is working fine. Below is the second POJO.

package com.edu.epam.graphQLSearch.custom.graphlQLqueries;

public class GqConfigCustomPOValue {


    public GqCustomPOCAList value;

    public GqCustomPOCAList getValue() {
        return value;
    }

    public void setValue(GqCustomPOCAList value) {
        this.value = value;
    }
}

Below is the third POJO

package com.edu.epam.graphQLSearch.custom.graphlQLqueries;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import java.util.List;


public class GqCustomPOCAList {


    public List<GqCustomPOConfigAttributes> config;

    public List<GqCustomPOConfigAttributes> getConfig() {
        return config;
    }

    public void setConfig(List<GqCustomPOConfigAttributes> config) {
        this.config = config;
    }
}

Below is the fourth POJO

package com.edu.epam.graphQLSearch.custom.graphlQLqueries;

public class GqCustomPOConfigAttributes {

    private String configurationName;
    private String key;
    private boolean mandatory;
    private String type;
    private Object options;

    public String getConfigurationName() {
        return configurationName;
    }

    public void setConfigurationName(String configurationName) {
        this.configurationName = configurationName;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public boolean isMandatory() {
        return mandatory;
    }

    public void setMandatory(boolean mandatory) {
        this.mandatory = mandatory;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public Object getOptions() {
        return options;
    }

    public void setOptions(Object options) {
        this.options = options;
    }
}

I am getting issue with POJO GqCustomPOCAList. Here, I am not able to define key "config-1" as it is mentioned in JSON.

When I define it as public List<GqCustomPOConfigAttributes> config; I get the error

Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "config-1" (class com.edu.epam.graphQLSearch.custom.graphlQLqueries.GqCustomPOCAList), not marked as ignorable (one known property: "config"])
 at [Source: (String)"{"customObject":{"value":{"config-1":{"configurationName":{"de":"Das ist ein Text","en":"Hospitalization Cover"},"key":"hospitalization-cover","mandatory":false,"type":"options","options":{"1Lakh":{"label":"1 Lakh","priceType":"fixed","price":"200","default":"true"},"2Lakh":{"label":"2 Lakh","priceType":"fixed","price":"400"},"3Lakh":{"label":"3 Lakh","priceType":"fixed","price":"450"},"4Lakh":{"label":"4 Lakh","priceType":"fixed","price":"500"},"5Lakh":{"label":"5 Lakh","priceType":"fixed","pri"[truncated 447 chars]; line: 1, column: 39] (through reference chain: com.edu.epam.graphQLSearch.custom.graphlQLqueries.GqCustomObject["customObject"]->com.edu.epam.graphQLSearch.custom.graphlQLqueries.GqConfigCustomPOValue["value"]->com.edu.epam.graphQLSearch.custom.graphlQLqueries.GqCustomPOCAList["config-1"])
    at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61)

I am not able to define variable as below in java class

public List<GqCustomPOConfigAttributes> config-1;

Is there any better way to handle this?


Solution

  • When you have multiple unknown fields, the best way to parse them is with the @JsonAnySetter annotation. This annotation allows you to add an unknown key-value pair within a specific field of your choice (usually a Map).

    Here is a working demo parsing your json at OneCompiler.

    Here is the list of suggestions/corrections in your code:

    class GqCustomPOCAList {
    
        // 1. Do not use a List to parse an unknown number of configs,
        // use instead the @JsonAnyGetter annotation and a Map to add every config with their name as the key (config-1, config-2, etc.)
        public Map<String, GqCustomPOConfigAttributes> config;
    
        // 2. initializing the Map so that it can be used by the method marked with @JsonAnySetter
        public GqCustomPOCAList(){
            config = new HashMap<>();
        }
    
        // 3. Method to add a generic config to the Map
        @JsonAnySetter
        public void addConfig(String cofigName, GqCustomPOConfigAttributes value){
            config.put(cofigName, value);
        }
    
        //... other methods ...
    }
    
    class GqCustomPOConfigAttributes {
    
        // 4. configurationName appears to be a Map<String, String> within your Json, not a String
        private Map<String, String> configurationName;
        private String key;
        private boolean mandatory;
        private String type;
    
        // 5. Do not use a generic Object to represent your option, define a class Option with the possible fields.
        // 6. Again, if the possible options are unknown, use a Map<String, Option> and the @JsonAnySetter annotation
        private Map<String, Option> options;
    
        // 7. initializing the Map so that it can be used by the method marked with @JsonAnySetter
        public GqCustomPOConfigAttributes(){
            configurationName = new HashMap<>();    
        }
    
        // 8. Method to add a generic option to the Map
        @JsonAnySetter
        public void addOption(String optionName, Option option){
            options.put(optionName, option);
        }
    
        // ... other methods ...
    }
    
    class Option {
        private String label;
        private String priceType;
        private BigDecimal price;
        private BigDecimal priceFactor;
    
        // 9. the 'default' property must be renamed as default is a java keyword,
        // use @JsonProperty to bind it to the field 'default' within the Json
        @JsonProperty("default")
        private boolean isDefault;
    
        // ... other methods ...
    }