1

I have a json file which contains an array of mixed types. Its structure look like this:

{
  "operations": [
    {
      "extension": {
        "serviceId": "id",
        "serviceType": "type"
      },
      "name": "name",
      "tags": "tags"
    },
    {
      "core": {
        "config": {
          "a": 90,
          "b": 45
        },
        "displayName": "displayName"
      },
      "name": "name",
      "tags": "tags"
    },
    {
      "extension": {
        "serviceId": "abc",
        "serviceType": "xyz"
      },
      "name": "name",
      "tags": "tags"
    }
  ]
}

Follow this topic Stackoverflow I create my model for json mapping of mixed types.

My java model:

@Data
public class Payload {

  private List<OperationElement> operations;

  @Data
  @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
  @JsonSubTypes({ @JsonSubTypes.Type(value = Extension.class, name = "extension"),
    @JsonSubTypes.Type(value = Core.class, name = "core") })
  public static abstract class OperationElement {
    private String name;
    private String tags;
  }

  @Data
  @JsonRootName("extension")
  public static class Extension extends OperationElement {
    private String serviceType;
    private String serviceId;
  }

  @Data
  @JsonRootName("core")
  public static class Core extends OperationElement {

    private Config config;
    private String displayName;

    @Data
    public static class Config {

      private Long a;
      private Long b;

    }
  }

  public static void main(String[] args) throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    Payload operationsPayload = mapper.readValue(
      Files.readAllBytes(Paths.get("C:\\desc", "file.json")), Payload.class);
    System.out.println(operationsPayload );
  }
}

I got this error when trying to parse the json to java model:

Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (FIELD_NAME), expected END_OBJECT: expected closing END_OBJECT after type information and deserialized value
  line: 8, column: 7] (through reference chain: com.pojo.Payload["operations"]->java.util.ArrayList[0])
    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
    at com.fasterxml.jackson.databind.DeserializationContext.wrongTokenException(DeserializationContext.java:1799)
    at com.fasterxml.jackson.databind.DeserializationContext.reportWrongTokenException(DeserializationContext.java:1533)
    at com.fasterxml.jackson.databind.jsontype.impl.AsWrapperTypeDeserializer._deserialize(AsWrapperTypeDeserializer.java:124)
    at com.fasterxml.jackson.databind.jsontype.impl.AsWrapperTypeDeserializer.deserializeTypedFromObject(AsWrapperTypeDeserializer.java:52)
    at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserializeWithType(AbstractDeserializer.java:263)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:349)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:28)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:324)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:187)
    at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4593)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3609)

If I remove

"name": "name",
"tags": "tags"

The code can run.

But I must have both these key-value in the json structure.

Any solution please suggest me. Thanks in advance.

SoT
  • 898
  • 1
  • 15
  • 36

1 Answers1

2

The JsonTypeInfo.As#WRAPPER_OBJECT works only for the serialization process while you are trying to deserialize your json input. In this case you can use the JsonTypeInfo.Id#DEDUCTION to deduce the correct type from the existing properties. Taking for example a simplified version of your json input like below:

{
  "operations": [
    {
      "extension": {
        "serviceId": "id",
        "serviceType": "type"
      },
      "name": "name",
      "tags": "tags"
    }
  ]
}

You have an array containing an anonymous object with the three properties extension, name, tag that can be deserialized like below:

@Data
public class Payload {
    List<OperationWrapper> operations;
}

@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION)
@JsonSubTypes({
    @JsonSubTypes.Type(value = ExtensionWrapper.class)
})
public abstract class OperationWrapper {}

@Data
public class ExtensionWrapper extends OperationWrapper {
    private Extension extension;
    private String name;
    private String tags;
}

@Data
public class Extension {
    private String serviceType;
    private String serviceId;
}

Payload payload = mapper.readValue(json, Payload.class);
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(payload));

Output:

{
  "operations" : [ {
    "extension" : {
      "serviceType" : "type",
      "serviceId" : "id"
    },
    "name" : "name",
    "tags" : "tags"
  } ]
}

The same mechanism can be applied to other classes you want to be deserialized.

dariosicily
  • 4,239
  • 2
  • 11
  • 17