如何使用jackson 2将set的子类序列化和解除分类为已知集合

时间:2018-06-11 09:15:31

标签: java serialization jackson jackson2

我正在尝试序列化一个jpa托管类(openjpa)。

此类包含一个集合。在运行时,这个集合的类型是org.apache.openjpa.util.java $ util $ LinkedHashSet $ proxy(我们正在使用openjpa)。

杰克逊将序列化此罚款,但在反序列化时将失败,因为无法构造此类型(并且当使用spring security的jackson配置时,它不会列入白名单。)

所以现在我认为解决方案是定制序列化,以便将其作为更标准的集合进行分类和反序列化。在反序列化时,只需要实现Set。

我想尝试避免污染持久化类(所以想要使用mixins)。

容器类是User类,它包含Role类的集合。 到目前为止,我有:

@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;

}

但是当我运行时,我得到了

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

序列化时会出现此错误。

所以它似乎没有尊重集合容器或其他东西。

1 个答案:

答案 0 :(得分:2)

所以我解决的答案是基于杰克逊如何不支持开箱即用的非标准集合。按理说,只有java sdk集合可以开箱即用。

所以我最终做的就是Spring安全性与它添加的非标准集合相同。

context.setMixInAnnotations(Collections.<Object>unmodifiableSet(Collections.emptySet()).getClass(), UnmodifiableSetMixin.class);

(来自org.springframework.security.jackson2.CoreJackson2Module)

所以我做了同样的事情:

context.setMixInAnnotations(org.apache.openjpa.util.java$util$LinkedHashSet$proxy.class, OpenJpaLinkedHashSetProxyMixin.class);

这意味着我可以自定义杰克逊如何对待这门课程。

现在杰克森序列化这个系列没问题。序列化问题一直存在。在序列化方面,杰克逊似乎支持任何子类Set。它正在反序列化中挣扎。

Spring确实为这个不可修改的集合提供了一个自定义的反序列化器,而这似乎是唯一可行的方法。

然后是mixin:

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonDeserialize(using = Deserializer.class)
public static class OpenJpaLinkedHashSetProxyMixin {

}

对从

复制的反序列化器进行微调
  

org.springframework.security.jackson2.UnmodifiableSetDeserializer

并且是:

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);
    }
}

我只是直接返回哈希集(例如,不将其包装在不可修改的集合中)。

我想底线是我不需要将set序列化回到它所来自的同一个类(org.apache.openjpa.util.java $ util $ LinkedHashSet $ proxy)所以只要它是一个Set什么实施并不重要。在这种情况下,我认为实现是HashSet。

我怀疑这是成功反序列化杰克逊不支持开箱即用的集合的唯一方法。