Search code examples
javaspringjacksonjson-deserialization

Json Deserializer - How to get data from parent JSON object


I am using Spring MVC and spring take care of converting json to Objects in controller. But my json structure is different then class structure. So I have written my own deserializer. But I am getting problem to accessing values of parent object in JSON. I think it is better to explain my problem using some example -

I have following JSON to deserialize

{
  id: 1,
  children: {
    "name1": "value1",
    "name2": "value2"
  }
}

I have following classes (Sample Code) -

public class Parent {
    private Integer id;

    @JsonDeserialize(using= ChildrenDeserializer.class)
    private List<Child> children;
    //... Getter/setters
}

public class Child {
    private Integer id;
    private String name;
    private String value;

    //...getters/setters
}

public class ChildrenDeserializer extends JsonDeserializer<List<Child>> {
    @Override
    public List<Child> deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        List<Child> children = new ArrayList<>();

        ObjectCodec oc = jsonParser.getCodec();
        JsonNode node = oc.readTree(jsonParser);
        Iterator<Map.Entry<String, JsonNode>> fieldsIterator = node.fields();

        while (fieldsIterator.hasNext()) {
            Map.Entry<String, JsonNode> field = fieldsIterator.next();
            String name = field.getKey();
            String value = field.getValue().textValue();

            Child child = new Child();

            //Here I want to get parentId from the Json. Is it possible??
            Integer childId = childRepository.searchChildIdByParentId(parentId, name);

            child.setId(childId);
            child.setName(name);
            child.setValue(value);
            children.add(child);
        }

        return children;
    }
}

Is there any way to get parentId (which is 1 in above example) while deserializing children??


Solution

  • Having the same need , I come across with your question . My solution to the problem is on the parent constructor: When create the parent class, set the correct property in the child class.

    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.testng.Assert;
    import org.testng.annotations.Test;
    
    import com.fasterxml.jackson.annotation.JsonCreator;
    import com.fasterxml.jackson.annotation.JsonIgnore;
    import com.fasterxml.jackson.annotation.JsonProperty;
    
    public class JsonUnitTest {
    
       private static final Logger logger = LogManager.getLogger(JsonUnitTest.class);
    
       @Test
       public void unit() {
    
          // test objects
          final int parentId = 1;
          final int childId = 2;
          final ParentClass parent = new ParentClass(parentId);
          final ChildClass child = new ChildClass(childId);
          parent.setChild(child);
          
          // serialize
          final String json = JsonHelper.toJson(parent);
          logger.info(json);
          logger.info(parent);
          
          // deserialize
          final ParentClass newParent = JsonHelper.fromJson(json, ParentClass.class);
    
          // asserts
          Assert.assertNotNull(newParent);
          Assert.assertEquals(newParent, parent);
    
       }
    

    The result of the test is

    [main] INFO JsonUnitTest:29 {"id":1,"child":{"childId":2}}
    [main] INFO JsonUnitTest:30 ParentClass [id=1, child=ChildClass [parentId=1, childId=2]]
    PASSED: unit
    

    Here the rest of the code :

       public static class ParentClass {
          
          public static final String idKey = "id";
          public static final String childKey = "child";
          
          @JsonProperty(idKey)
          private int id;
          
          @JsonProperty(childKey)
          private ChildClass child;
          
          public ParentClass(int id) {
             this.id = id;
          }
    
          @JsonCreator
          public ParentClass(
                @JsonProperty(idKey) int id, 
                @JsonProperty(childKey) ChildClass child) {
             super();
             this.id = id;
             setChild(child);
          }
    
          @JsonProperty(idKey)
          public int getId() {
             return id;
          }
    
          @JsonProperty(idKey)
          public void setId(int id) {
             this.id = id;
          }
    
          @JsonProperty(childKey)
          public ChildClass getChild() {
             return child;
          }
          
          @JsonProperty(childKey)
          public void setChild(ChildClass child) {
             this.child = child;
             child.setParentId(id);
          }
    
          @Override
          public int hashCode() {
             final int prime = 31;
             int result = 1;
             result = prime * result + ((child == null) ? 0 : child.hashCode());
             result = prime * result + id;
             return result;
          }
    
          @Override
          public boolean equals(Object obj) {
             if (this == obj)
                return true;
             if (obj == null)
                return false;
             if (getClass() != obj.getClass())
                return false;
             ParentClass other = (ParentClass) obj;
             if (child == null) {
                if (other.child != null)
                   return false;
             } else if (!child.equals(other.child))
                return false;
             if (id != other.id)
                return false;
             return true;
          }
    
          @Override
          public String toString() {
             return "ParentClass [id=" + id + ", child=" + child + "]";
          }
       }
       
       public static class ChildClass {
          
          public static final String parentIdKey = "../" + ParentClass.idKey;
          public static final String childIdKey = "childId";
          
          private int parentId;
          private int childId;
          
          @JsonCreator
          public ChildClass(@JsonProperty(childIdKey) int childId) {
             super();
             this.childId = childId;
          }
    
          @JsonIgnore
          public int getParentId() {
             return parentId;
          }
    
          @JsonProperty(parentIdKey)
          public void setParentId(int parentId) {
             this.parentId = parentId;
          }
    
          @JsonProperty(childIdKey) 
          public int getChildId() {
             return childId;
          }
    
          @JsonProperty(childIdKey) 
          public void setChildId(int childId) {
             this.childId = childId;
          }
    
          @Override
          public int hashCode() {
             final int prime = 31;
             int result = 1;
             result = prime * result + childId;
             result = prime * result + parentId;
             return result;
          }
    
          @Override
          public boolean equals(Object obj) {
             if (this == obj)
                return true;
             if (obj == null)
                return false;
             if (getClass() != obj.getClass())
                return false;
             ChildClass other = (ChildClass) obj;
             if (childId != other.childId)
                return false;
             if (parentId != other.parentId)
                return false;
             return true;
          }
    
          @Override
          public String toString() {
             return "ChildClass [parentId=" + parentId + ", childId=" + childId + "]";
          }
       }
    }