Search code examples
javaspringspring-bootjacksonjackson-databind

Improved way to convert nested JSON object's boolean values into a Map with Jackson


I have the following JSON object

{
  "donor": "Y",
  "bloodType": null,
  "eligibility": {
    "categoryEligible": false,
    "suspensionEligible": false,
    "paidFinesEligible": false,
    "pointSystemEligible": false,
    "failedDocuments": [
      {
        "type": "SOMETHING",
        "reason": "SOMETHING_ELSE"
      }
    ],
    "eligible": false,
  }
}

I'm using Jackson to convert it into my domain object. Here are the fields I'm using:

    private String donor;

    @JsonProperty("eligibility")
    private Eligibility eligibility;

The Eligibility class contains all these fields, I want to instead of having individual fields for all the boolean values, to have a single Map< String, Bolean > where String is the property name and boolean is the value.



    @JsonProperty("failedDocuments")
    private List<FailedDocumentsItem> failedDocuments;

    @JsonProperty("eligible")
    private boolean eligible;

    @JsonProperty("donor")
    private boolean donor;


Solution

  • Add an @JsonAnySetter field (Jackson 2.8+) or method:

    Marker annotation that can be used to define a logical "any setter" mutator -- either using non-static two-argument method (first argument name of property, second value to set) or a field (of type Map or POJO) - to be used as a "fallback" handler for all otherwise unrecognized properties found from JSON content.

    Example using public fields for brevity.

    public class Test {
        public static void main(String[] args) throws Exception {
            ObjectMapper mapper = new ObjectMapper();
            Root root = mapper.readValue(new File("test.json"), Root.class);
            System.out.println("donor = " + root.donor);
            System.out.println("flags = " + root.eligibility.flags);
            System.out.println("failedDocuments = " + root.eligibility.failedDocuments);
        }
    }
    class Root {
        public Boolean realId;
        public String donor;
        public Boolean bloodType;
        public Boolean selectiveServiceCandidate;
        public Eligibility eligibility;
    }
    class Eligibility {
        @JsonAnySetter
        public Map<String, Boolean> flags = new HashMap<>();
        public List<FailedDocument> failedDocuments;
    }
    class FailedDocument {
        public String type;
        public String reason;
        @Override
        public String toString() {
            return "FailedDocument[type=" + this.type + ", reason=" + this.reason + "]";
        }
    }
    

    Output

    donor = Y
    flags = {paidFinesEligible=false, hasRealId=false, suspensionEligible=false, acaaEligible=false, eligibleIgnoreRenewalDate=false, eligibleDocuments=false, cardStatusEligible=false, expirationDateEligible=false, eligible=false, citizenEligible=false, pointSystemEligible=false, ageEligible=false, gravamenesEligible=false, categoryEligible=false, eligibleMedical=false}
    failedDocuments = [FailedDocument[type=CERTIFICATE_CITIZENSHIP, reason=MISSING]]