1

I have situation where I hI was trying to map a JSON to Java Object which consists of nested objects with the dynamic key. I tried to map it to LinkedHashMap but it failed to do so.

Below is the JSON output I am trying to map:

{
"success": true,
"item": {
"id": 8903,
"firstLevel": {
  "266940": {
    "SecondLevel": {
      "407617": {
        "thirdLevel": {
          "2381098": {
            "outcome": "PASS",
            "screenshot": "2617.png",
            "dataRow": 0
          },
          "2381134": {
            "outcome": "PASS",
            "screenshot": "06d.png",
            "dataRow": 0
          }
        }
      },
      "407620": {
        "thirdLevel": {
          "2381043": {
            "outcome": "PASS",
            "screenshot": "2617.png",
            "dataRow": 0
          },
          "2381198": {
            "outcome": "PASS",
            "screenshot": "06d.png",
            "dataRow": 0
          }
        }
      }
    }
  } ,
"266941": {
    "SecondLevel": {
      "407617": {
        "thirdLevel": {
          "2381094": {
            "outcome": "PASS",
            "screenshot": "2617.png",
            "dataRow": 0
          },
          "2381138": {
            "outcome": "PASS",
            "screenshot": "06d.png",
            "dataRow": 0
          }
        }
      },
      "407620": {
        "thirdLevel": {
          "2381047": {
            "outcome": "PASS",
            "screenshot": "2617.png",
            "dataRow": 0
          },
          "2381191": {
            "outcome": "PASS",
            "screenshot": "06d.png",
            "dataRow": 0
          }
        }
      }
    }
  }
},
"outcome": "PASS",
"status": "FINISHED"
 }
}

I also used @JsonAnySetter to map the dynamic value to the LinkedHashMap but this also didn't help.

 private Map<String, FirstLevel> firstLevel = new LinkedHashMap<>();

 public Map<String, FirstLevel> getFirstLevel() {
  return firstLevel;
 }

 public void setFirstLevel(Map<String, FirstLevel> firstLevel) {
  this.firstLevel = firstLevel;
 }

 @JsonAnySetter
 public void setFirstLevel(String key, FirstLevel value) {
  this.firstLevel.put(key, value);
 }

Below is the code for the firstLevel Object

  private Map<String, FirstLevel> firstLevel = new LinkedHashMap<>();

  public Map<String, FirstLevel> getFirstLevel() {
   return firstLevel;
  }

  public void setFirstLevel(Map<String, FirstLevel> firstLevel) {
   this.firstLevel = firstLevel;
  }
Sujit kumar
  • 131
  • 10
  • Please read "How to create a [mcve]". Then use the [edit] link to improve your question (do not add more information via comments). Otherwise we are not able to answer your question and help you. Please add the code you wrote yourself. "didnt help" isnt a problem description we could help with much. – GhostCat Mar 05 '19 at 13:19

1 Answers1

1

You need to use @JsonAnySetter on first level and @JsonCreator on each next. Below you can find whole working solution. It is a little bit complicated because of wrapper objects you have on each level. To skip them we can write custom deserialiser or use JsonCreator in constructor:

import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.File;
import java.util.Map;

public class JsonApp {

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

        ObjectMapper mapper = new ObjectMapper();
        Response response = mapper.readValue(jsonFile, Response.class);

        response.getItem().getNextLevel().forEach((k, v) -> {
            System.out.println(k);
            v.getNextLevel().forEach((k1, v1) -> {
                System.out.println("\t" + k1);
                v1.getNextLevel().forEach((k2, v2) -> {
                    System.out.println("\t\t" + k2 + " => " + v2);
                });
            });
        });
    }
}

class Response {

    private boolean success;
    private Item item;

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public Item getItem() {
        return item;
    }

    public void setItem(Item item) {
        this.item = item;
    }

    @Override
    public String toString() {
        return "Response{" +
                "success=" + success +
                ", item=" + item +
                '}';
    }

    public static <T> Map<String, T> getOne(Map<String, Map<String, T>> map) {
        if (map == null || map.size() == 0) {
            return null;
        }

        return map.values().iterator().next();
    }
}

class Item {

    private int id;
    private String outcome;
    private String status;
    private Map<String, SecondLevel> nextLevel;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getOutcome() {
        return outcome;
    }

    public void setOutcome(String outcome) {
        this.outcome = outcome;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public Map<String, SecondLevel> getNextLevel() {
        return nextLevel;
    }

    @JsonAnySetter
    public void set(String property, Map<String, SecondLevel> map) {
        nextLevel = map;
    }

    @Override
    public String toString() {
        return "Item{" +
                "id=" + id +
                ", outcome='" + outcome + '\'' +
                ", status='" + status + '\'' +
                ", firstLevel=" + nextLevel +
                '}';
    }
}

class SecondLevel {

    private Map<String, OutcomeLevel> nextLevel;

    @JsonCreator
    public SecondLevel(Map<String, Map<String, OutcomeLevel>> map) {
        this.nextLevel = Response.getOne(map);
    }

    public Map<String, OutcomeLevel> getNextLevel() {
        return nextLevel;
    }

    @Override
    public String toString() {
        return "SecondLevel{" +
                "nextLevel=" + nextLevel +
                '}';
    }
}

class OutcomeLevel {

    private Map<String, Outcome> nextLevel;

    @JsonCreator
    public OutcomeLevel(Map<String, Map<String, Outcome>> map) {
        this.nextLevel = Response.getOne(map);
    }

    public Map<String, Outcome> getNextLevel() {
        return nextLevel;
    }

    @Override
    public String toString() {
        return "OutcomeLevel{" +
                "nextLevel=" + nextLevel +
                '}';
    }
}

class Outcome {

    private String outcome;
    private String screenshot;
    private int dataRow;

    public String getOutcome() {
        return outcome;
    }

    public void setOutcome(String outcome) {
        this.outcome = outcome;
    }

    public String getScreenshot() {
        return screenshot;
    }

    public void setScreenshot(String screenshot) {
        this.screenshot = screenshot;
    }

    public int getDataRow() {
        return dataRow;
    }

    public void setDataRow(int dataRow) {
        this.dataRow = dataRow;
    }

    @Override
    public String toString() {
        return "Outcome{" +
                "outcome='" + outcome + '\'' +
                ", screenshot='" + screenshot + '\'' +
                ", dataRow=" + dataRow +
                '}';
    }
}

Above code prints:

266940
    407617
        2381098 => Outcome{outcome='PASS', screenshot='2617.png', dataRow=0}
        2381134 => Outcome{outcome='PASS', screenshot='06d.png', dataRow=0}
    407620
        2381043 => Outcome{outcome='PASS', screenshot='2617.png', dataRow=0}
        2381198 => Outcome{outcome='PASS', screenshot='06d.png', dataRow=0}
266941
    407617
        2381094 => Outcome{outcome='PASS', screenshot='2617.png', dataRow=0}
        2381138 => Outcome{outcome='PASS', screenshot='06d.png', dataRow=0}
    407620
        2381047 => Outcome{outcome='PASS', screenshot='2617.png', dataRow=0}
        2381191 => Outcome{outcome='PASS', screenshot='06d.png', dataRow=0}

See also:

Michał Ziober
  • 37,175
  • 18
  • 99
  • 146