【问题标题】:How to serializer and deseralize a sub class of set as a known set with jackson 2如何使用jackson 2将集合的子类序列化和反序列化为已知集合
【发布时间】:2018-11-20 11:53:48
【问题描述】:

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

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

Jackson 将对此进行序列化,但在反序列化时将失败,因为无法构造此类型(并且当使用 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

而且在序列化的时候会出现这个错误。

所以它似乎不尊重设置的容器或其他东西。

【问题讨论】:

  • 为什么不能构造。我刚刚成功执行了这个: new org.apache.openjpa.util.java$util$LinkedHashSet$proxy();
  • 这是真的 - 这是一个有效的类等。但我怀疑它不起作用的原因是杰克逊不承认它是一个集合。它支持默认的集合类,而不是自定义的。

标签: java serialization jackson jackson2


【解决方案1】:

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

所以我最终所做的与 Spring Security 对其添加的非标准集合所做的相同。

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

这意味着我可以自定义 jackson 对待此类的方式。

现在杰克逊序列化这个集合没有问题。问题一直与序列化有关。在序列化方面,Jackson 似乎支持 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);
    }
}

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

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

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

【讨论】:

    猜你喜欢
    • 2016-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-24
    • 1970-01-01
    • 2021-03-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多