V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
JasonLaw
V2EX  ›  Java

Jackson 序列化时,如何将 final 类型的类型信息保存起来?

  •  
  •   JasonLaw · Jul 10, 2020 · 3414 views
    This topic created in 2125 days ago, the information mentioned may be changed or developed.

    先看一下以下代码了解问题

    ObjectMapper objectMapper = new ObjectMapper()
                    .enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    
    //        Map<Integer, Integer> map1 = new HashMap<>();
    //        map1.put(1, 1);
            Map<Integer, Integer> map1 = ImmutableMap.of(1, 1);
            
            // 使用 HashMap 时,content 是`["java.util.HashMap",{"1":1}]`;而使用 ImmutableMap 时是`{"1":1}`
            String content = objectMapper.writeValueAsString(map1);
            // 因为使用 ImmutableMap 时没有了类型信息,反序列化会报错。
            // com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class java.util.Map
     at [Source: (String)"{"1":1}"; line: 1, column: 1]
            Map<Integer, Integer> map2 = objectMapper.readValue(content, new TypeReference<Map<Integer, Integer>>() {
            });
    

    按照JacksonPolymorphicDeserialization · FasterXML/jackson-docs Wiki所描述的,如果我没有理解错的话,使用 ImmutableMap 时没有保存类型信息是因为ImmutableMap.of(1, 1)会产生SingletonImmutableBiMap,而SingletonImmutableBiMap是 final 的。

    但是四种ObjectMapper.DefaultTyping( JAVA_LANG_OBJECT, OBJECT_AND_NON_CONCRETE, NON_CONCRETE_AND_ARRAYS, NON_FINAL )都无法实现“保存SingletonImmutableBiMap这个类型信息”。而ObjectMapper.setDefaultTyping(...)也是依赖于ObjectMapper.DefaultTyping的,所以也不行。

    问题:Jackson 能够保存 final 类型的类型信息吗?如果可以的话,应该怎么做呢?

    6 replies    2020-07-10 16:24:55 +08:00
    6IbA2bj5ip3tK49j
        1
    6IbA2bj5ip3tK49j  
       Jul 10, 2020
    试过 activateDefaultTypingAsProperty 吗?
    6IbA2bj5ip3tK49j
        2
    6IbA2bj5ip3tK49j  
       Jul 10, 2020
    改成 ObjectMapper.DefaultTyping.EVERYTHING,就能保存了。
    ["com.google.common.collect.SingletonImmutableBiMap",{"1":1}]
    但是没办法反序列化,因为这玩意儿没有构造器。
    JasonLaw
        3
    JasonLaw  
    OP
       Jul 10, 2020
    @xgfan #2 谢谢提示🙏

    改变后的代码如下:

    PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
    .allowIfSubType(ImmutableMap.class)
    .build();
    ObjectMapper objectMapper = new ObjectMapper()
    .activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.EVERYTHING);

    // Map<Integer, Integer> map1 = new HashMap<>();
    // map1.put(1, 1);
    Map<Integer, Integer> map1 = ImmutableMap.of(1, 1);

    // content 为`["com.google.common.collect.SingletonImmutableBiMap",{"1":1}]`
    String content = objectMapper.writeValueAsString(map1);
    // 但是因为 SingletonImmutableBiMap 没有默认的构造器,反序列化报错
    // com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.google.common.collect.SingletonImmutableBiMap` (no Creators, like default constructor, exist): no default constructor found
    at [Source: (String)"["com.google.common.collect.SingletonImmutableBiMap",{"1":1}]"; line: 1, column: 54]
    Map<Integer, Integer> map2 = objectMapper.readValue(content, new TypeReference<Map<Integer, Integer>>() {
    });
    JasonLaw
        4
    JasonLaw  
    OP
       Jul 10, 2020
    我现在的处理方式是使用 HashMap 作为“中间人”来实现 ImmutableMap 的序列化和反序列化。参考 https://stackoverflow.com/a/34115875/5232255
    azygote
        5
    azygote  
       Jul 10, 2020 via iPhone   ❤️ 1
    你需要这个包 https://github.com/FasterXML/jackson-datatypes-collections 来进行 Guava 里面的一些 Collections 类的序列 /反序列化
    JasonLaw
        6
    JasonLaw  
    OP
       Jul 10, 2020
    @azygote #5 谢谢🙏

    最后的代码为:

    PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
    .allowIfSubType(ImmutableMap.class)
    .build();
    ObjectMapper objectMapper = JsonMapper.builder()
    .addModule(new GuavaModule())
    .activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.EVERYTHING)
    .build();

    Map<Integer, Integer> map1 = ImmutableMap.of(1, 1);

    // content 为`["com.google.common.collect.SingletonImmutableBiMap",{"1":1}]`
    String content = objectMapper.writeValueAsString(map1);
    // 能够成功反序列化
    Map<Integer, Integer> map2 = objectMapper.readValue(content, new TypeReference<Map<Integer, Integer>>() {
    });
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   1397 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 42ms · UTC 16:31 · PVG 00:31 · LAX 09:31 · JFK 12:31
    ♥ Do have faith in what you're doing.