6

I have JSON structured like:

{
  "id" : "123",
  "name" : [ {
    "stuff" : [ {
      "id" : "234",
      "name" : "Bob"
    }, {
      "id" : "345",
      "name" : "Sally"
    } ]
  } ]
}

That I want to map to the following data structure:

MyInterface1

@Value.Immutable
@JsonSerialize(as = ImmutableMyInterface1.class)
@JsonDeserialize(as = ImmutableMyInterface1.class)
public interface MyInterface1 {
    String id();
    @JsonDeserialize(using = MyInterface1Deserializer.class)
    List<MyInterface2> name();
}

MyInterface2

@Value.Immutable
@JsonSerialize(as = ImmutableMyInterface2.class)
@JsonDeserialize(as = ImmutableMyInterface2.class)
public interface MyInterface2 {
    @JsonDeserialize(using = StuffDeserializer.class)
    Map<String, MyInterface3> stuff();
}

MyInterface3

@Value.Immutable
@JsonSerialize(as = ImmutableMyInterface3.class)
@JsonDeserialize(as = ImmutableMyInterface3.class)
public interface MyInterface3 {
      String id();
      String name();
}

I'm using an ObjectMapper with readValue(stringWithJson,MyInterface1.class) to map this JSON to MyInterface1, which should continue down the chain to MyInterface3. This setup was working when I was using a List in MyInterface2, i.e. List<MyInterface3> name();

However, I want this to be a map instead of a list, ideally with "id" from the inner JSON as the key. This would allow me to get values with the following syntax: MyInterface1.get(0).MyInterface2.get("id1").name();

The problem is that when attempting to create a custom StuffDeserializer.class, I'm getting the error: Can not deserialize instance of com.foo.ImmutableMyInterface2$Json out of START_ARRAY token

when trying to do:

public Map<String, MyInterface3> deserialize(JsonParser jsonParser, DeserializationContext ctxt)
        throws IOException {

    MyInterface2 foo = Unmarshaller.OBJECT_MAPPER.readValue(jsonParser, MyInterface2.class); // error here
    ...

I think this is because Jackson is expecting "stuff" to be a List 'cause of the JSON array. What's the best way to deserialize this JSON to a map that uses values from the inner JSON as a key?

1 Answer 1

5

I would create a custom JsonDeserializer to map id and name into a map:

public class StringHashMapValueDeserializer extends JsonDeserializer<HashMap<String, String>>{

    @Override
    public HashMap<String, String> deserialize(JsonParser parser, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        HashMap<String, String> ret = new HashMap<String, String>();

        ObjectCodec codec = parser.getCodec();
        TreeNode node = codec.readTree(parser);

        if (node.isArray()){
            for (JsonNode n : (ArrayNode)node){
                JsonNode id = n.get("id");
                if (id != null){
                    JsonNode name = n.get("name");
                    ret.put(id.asText(), name.asText());
                }
            }
        }
        return ret;
    }
}

And then I would create simple beans with annotating stuff property with the deserializer:

@Getter
@Setter
public class Name {

    @JsonDeserialize(using = StringHashMapValueDeserializer.class)
    Map<String, String> stuff;

    @Override
    public String toString() {
        return "Name [stuff=" + stuff + "]";
    }
}

Outer type:

@Getter
@Setter
public class OuterType {

    String id;
    List<Name> name;

    @Override
    public String toString() {
        return "OuterType [id=" + id + ", stuff=" + name + "]";
    }
}

Deserialization:

ObjectMapper mapper = new ObjectMapper();

OuterType response;
response = mapper.readValue(json, OuterType.class);

System.out.println(response);
System.out.println(response.getName().get(0).getStuff().get("234"));

console output:

OuterType [id=123, stuff=[Name [stuff={234=Bob, 345=Sally}]]]
Bob

Hope it helps.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.