Reputation: 21184
I am trying to serializde a jpa managed class (openjpa).
This class contains a set. At runtime this set's type is org.apache.openjpa.util.java$util$LinkedHashSet$proxy (we're using openjpa).
Jackson will serialize this fine but then will fail when it comes to deserialize as this type cannot be constructed (and when using spring security's jackson config, it is not whitelisted).
So now I think the solution is to customise the serialization so it is serailized and deserialized as a more standard set. When it is deserialized it need only implement Set.
And I want to try and avoid polluting the persistent class (so want to use mixins).
The container class is the User class and it contains set of the Role class. So far I have:
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)
public static class UserMixin {
@JsonDeserialize(as = LinkedHashSet.class, contentAs = LinkedHashSet.class)
@JsonSerialize(as = LinkedHashSet.class, contentAs = LinkedHashSet.class, typing = Typing.DYNAMIC)
private Set<Role> roles;
}
But when I run with this I get
Invalid definition for property roles (of type 'Lxxx/yyy/User;'): Can not refine serialization content type [simple type, class xxx.yyy.Role] into java.util.LinkedHashSet; types not related
And this error occurs when serializing.
So it seems like it is not honouring the set container or something.
Upvotes: 2
Views: 988
Reputation: 21184
So the answer that I settled is on is based on how jackson does not support non standard collections out of the box. It stands to reason that only java sdk collections would work out of the box.
So what I ended up doing is the same thing that spring security does with the non standard collections it adds.
context.setMixInAnnotations(Collections.<Object>unmodifiableSet(Collections.emptySet()).getClass(), UnmodifiableSetMixin.class);
(from org.springframework.security.jackson2.CoreJackson2Module)
So I did the same:
context.setMixInAnnotations(org.apache.openjpa.util.java$util$LinkedHashSet$proxy.class, OpenJpaLinkedHashSetProxyMixin.class);
This means that I can customise how jackson treats this class.
Now jackson serializes this collection no problem. The problem has ever been with serialization. Jackson seems to support any sub class of Set when it comes to serialization. It is on deserialization that it struggles.
That Spring does is provide a custom deserializer for that unmodifiable set and that seems to be the only way to go with this.
So then the mixin:
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonDeserialize(using = Deserializer.class)
public static class OpenJpaLinkedHashSetProxyMixin {
}
Where minor adjustments are made on the deserializer copied from
org.springframework.security.jackson2.UnmodifiableSetDeserializer
and is:
class UnmodifiableSetDeserializer extends JsonDeserializer<Set> {
@Override
public Set deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
JsonNode node = mapper.readTree(jp);
Set<Object> resultSet = new HashSet<Object>();
if (node != null) {
if (node instanceof ArrayNode) {
ArrayNode arrayNode = (ArrayNode) node;
Iterator<JsonNode> nodeIterator = arrayNode.iterator();
while (nodeIterator.hasNext()) {
JsonNode elementNode = nodeIterator.next();
resultSet.add(mapper.readValue(elementNode.traverse(mapper), Object.class));
}
} else {
resultSet.add(mapper.readValue(node.traverse(mapper), Object.class));
}
}
return Collections.unmodifiableSet(resultSet);
}
}
I just return the hash set directly (without wrapping it in the unmodifiable set) for example.
I guess the bottom line is that I do not need the set serialized back into the same class it came from (org.apache.openjpa.util.java$util$LinkedHashSet$proxy) so as long as it is a Set it doesn't matter what implementation. In this case I think the implementation is HashSet.
And I suspect this is the recommended/only way to successfully deserialize collections that jackson does not support out of the box.
Upvotes: 2