1

If I have a class

class DTO {
    final MySet<Types> values = MySetWrapper(EnumSet.of(Types.class));

    public MySet getValues() {
        return values;
    }
}

where MySet extends Set. Jackson complains that

Cannot find a deserializer for non-concrete Collection type MySet

which I understand, but I already instantiated the collection. What I want is for jackson to just call add for each value after it created an instance, something like:

DTO o = new DTO();
MySet<Types> values = o.getValues();
for (Types type : jsonArray) {
    values.add(type );
}

I don't want it to try to create a new collection itself.

Mark
  • 2,167
  • 4
  • 32
  • 64
  • Is adding a custom deserializer an option ? – Coder Jul 31 '19 at 13:57
  • @Coder Yes. I have a solution currently that looks like the accepted answer [here](https://stackoverflow.com/questions/36159677/how-to-create-a-custom-deserializer-in-jackson-for-a-generic-type). But even there in the `deserialize` method I need to create the instance *again* myself. Also I don't want ALL of my `MySet` to be instantiated that way. I could have a `MySetWrapper` wrap some other set implementation. Regadless of what's on the right side, the left one is a set and should be treated as a regular set. – Mark Jul 31 '19 at 14:01
  • Have you tried using @JsonCreator for your DTO class? With a factory method/ constructor annotated as a creator in Jackson, you can define the object deserialization strategy for DTO.class. Here's a simple example : https://www.logicbig.com/tutorials/misc/jackson/json-creator.html – Arpan Kanthal Jul 31 '19 at 14:30
  • @ArpanKanthal the problem is that as much as i know if i do that i would need to manually set all the other fields in my class which are otherwise fine. I want only special treatment for this field, not the whole class. – Mark Jul 31 '19 at 15:30
  • 1
    @Mark another way i can think of is using a @ JsonSetter annotation to do what you need to . Let me know if that helps – Arpan Kanthal Jul 31 '19 at 15:57
  • @ArpanKanthal I managed to make it work with `@JsonProperty`, thanks. – Mark Jul 31 '19 at 18:52

2 Answers2

1

That error message means that the DTO class is configured (by default or explicitly) to deserialize the values part of the JSON input into the DTO values field of DTO :

Cannot find a deserializer for non-concrete Collection type MySet

If you consider that Jackson should not perform the deserialization directly on this field, you could define a constructor to set values and also make sure that Jackson will not perform automatically the deserialization work : to achieve it, remove setter for that field (or add @JsonIgnore on it) and any jackson module that will use reflection to deserialize to fields.

It would give :

final MySet<Types> values = MySetWrapper(EnumSet.of(Types.class));

@JsonCreator
public MyFoo(Set<Types> values) { 
    this.values.addAll(values);
}

Note that I specified in the constructor Set and not MySet (should not be an issue as interface doesn't declare fields), otherwise you would get the same issue since you didn't define a deserializer for MySet.
But if you implement a deserializer for that you could of course do :

public MyFoo(MySet<Types> values) { 
    this.values.addAll(values);
}
davidxxx
  • 125,838
  • 23
  • 214
  • 215
  • "remove setter for that field" it's a final field, there is no setter, only getter. – Mark Jul 31 '19 at 15:05
  • Also what about the other fields I have in my class that work fine. If I use this custom constructor would I need to manually set all the other fields, or does jackson know to set after construction everything else? – Mark Jul 31 '19 at 15:20
  • You should very probably do that. – davidxxx Jul 31 '19 at 15:26
0

Found an answer using @JsonProperty:

@JsonProperty
private void setValues(Set<Types> types) {
    values.addAll(types);
}

Pretty short and simple thankfully.

Edit: seems like you don't even need the annotation.

Mark
  • 2,167
  • 4
  • 32
  • 64