0

I am trying to write code that uses Jackson to serialize/deserialize the objects.

The objects are Polymorphic in nature:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "name")
@JsonSubTypes({
    @Type(value = ComparableQuery.class),
    @Type(value = CompositeQuery.class)
})
public abstract class BaseQuery {

    private final Long characteristicId;

...
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "name")
public class CompositeQuery extends BaseQuery {

    private final String operator;
    private final BaseQuery[] queries;

    public CompositeQuery(Long characteristicId, Operator operator, BaseQuery... queries) {
        super(characteristicId);
        this.operator = operator.value;
        this.queries = queries;
    }

...
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "name")
@JsonSubTypes({
    @Type(value = EqualQuery.class),
    @Type(value = GreaterOrEqualQuery.class),
    @Type(value = GreaterQuery.class),
    @Type(value = LessOrEqualQuery.class),
    @Type(value = LessQuery.class)
})
public abstract class ComparableQuery extends BaseQuery {

    private final Object value;

    private final String comparisonOperator;

...
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "name")
public class EqualQuery extends ComparableQuery {

    public EqualQuery(Long characteristicId, Object value) {
        super(characteristicId, value, "=");
    }

}

I have created a Set<BaseQuery> with a following code:

    Set<BaseQuery> queries = new HashSet<>();

    BaseQuery megapixelCharacteristicQuery = new CompositeQuery(megapixelCharacteristic.getCharacteristicId(), CompositeQuery.Operator.AND, new GreaterOrEqualQuery(megapixelCharacteristic.getCharacteristicId(), 10), new LessOrEqualQuery(megapixelCharacteristic.getCharacteristicId(), 50));
    queries.add(megapixelCharacteristicQuery);

Right now when I try to serialize the object I receive a following JSON:

[
   {
      "characteristicId":391,
      "operator":"AND",
      "queries":[
         {
            "name":"GreaterOrEqualQuery",
            "characteristicId":391,
            "value":10,
            "comparisonOperator":">="
         },
         {
            "name":"LessOrEqualQuery",
            "characteristicId":391,
            "value":50,
            "comparisonOperator":"<="
         }
      ]
   }
]

but when I try to deserialize the JSON document I'm receiving the following exception:

com.fasterxml.jackson.databind.JsonMappingException: Unexpected token (END_OBJECT), expected FIELD_NAME: missing property 'name' that is to contain type id  (for class com.example.decision.query.characteristic.BaseQuery)
 at [Source: [{"characteristicId":391,"operator":"AND","queries":[{"name":"GreaterOrEqualQuery","characteristicId":391,"value":10,"comparisonOperator":">="},{"name":"LessOrEqualQuery","characteristicId":391,"value":50,"comparisonOperator":"<="}]}]; line: 1, column: 233] (through reference chain: java.util.HashSet[0])
    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:261)

For a some reason the name field is absent in the JSON root object.

How to fix it ?

UPDATED

It is working fine when I try to serialize only for example megapixelCharacteristicQuery object:

BaseQuery megapixelCharacteristicQuery = new CompositeQuery(megapixelCharacteristic.getCharacteristicId(), CompositeQuery.Operator.AND, new GreaterOrEqualQuery(megapixelCharacteristic.getCharacteristicId(), 10), new LessOrEqualQuery(megapixelCharacteristic.getCharacteristicId(), 50));

In this case Jackson forms the following JSON(with a correct "name":"CompositeQuery"):

{
   "name":"CompositeQuery",
   "characteristicId":391,
   "operator":"AND",
   "queries":[
      {
         "name":"GreaterOrEqualQuery",
         "characteristicId":391,
         "value":10,
         "operator":">="
      },
      {
         "name":"LessOrEqualQuery",
         "characteristicId":391,
         "value":50,
         "operator":"<="
      }
   ]
}

but serialization/deserialization still doesn't work when megapixelCharacteristicQuery is placed inside of HashSet<BaseQuery>.

How to make it work with HashSet ?

Also, it is start working fine even with a HashSet when I add defaultImpl = CompositeQuery.class to JsonTypeInfo annotation, for example:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "name", defaultImpl = CompositeQuery.class)
@JsonSubTypes({
    @Type(value = ComparableQuery.class),
    @Type(value = CompositeQuery.class)
})
public abstract class BaseQuery {
...
}

but it is not an option to me because I don't know what type should be used in different cases so I'm still look for a solution how to correctly provide name parameter in my JSON.

alexanoid
  • 24,051
  • 54
  • 210
  • 410
  • did you check that name is set on the object your trying to serialize ? – Ran Koretzki Dec 23 '16 at 13:04
  • As you can see from my question, the name field is only set on the nested objects – alexanoid Dec 23 '16 at 13:25
  • But for a some reason the name field is not set for `CompositeQuery` object and right now I don't understand why.. – alexanoid Dec 23 '16 at 14:02
  • If it is not set on the Java object Jackson won't serialize that field in JSON. Jackson will try to deserialize by defualt all attributes as mandatory unless marked with @JsonInclude(JsonInclude.Include.NON_EMPTY) annotation, in which it won't through this exception. – Ran Koretzki Dec 23 '16 at 14:09
  • `@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "name")` annotation should automatically add one extra field `name` to the serialized object as it has been done for example for nested `GreaterOrEqualQuery` object(please see the JSON in my question).. but this property is absent for a some unknown reason for the his owner - `CompositeQuery` – alexanoid Dec 23 '16 at 14:20

1 Answers1

2

Finally, thanks to the answer provided in the following question Why does Jackson polymorphic serialization not work in lists? I have found a solution:

Set<BaseQuery> queries = new HashSet<BaseQuery>() {
};

did the trick. Now everything works as expected.

Community
  • 1
  • 1
alexanoid
  • 24,051
  • 54
  • 210
  • 410