【问题标题】:Jackson Deserializer delegate to next applicable deserializerJackson Deserializer 委托给下一个适用的反序列化器
【发布时间】:2019-03-28 11:12:42
【问题描述】:

我有一个用于查询一些数据的外部服务。数据将采用以下两种格式之一(第一种是“遗留”,但需要支持):

{
    "foo": "John Smith"
}

{
    "foo": {
        "name": "John Smith",
        "bar": "baz"
    }
}

我想映射到以下 POJO:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Outer {

    private Foo foo;

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Foo {

        String name;
        String bar;

    }

}

第二种格式的数据(foo 是一个对象)应该像任何其他 POJO 一样反序列化,但是给定第一种格式的数据(foo 是字符串),将其转换为Foo 的实例, 我想打电话给new Foo(<foo>, null)。为此,我创建了一个自定义反序列化器(@JsonComponent 意味着这个反序列化器将在 spring 之前通过 Jackson Module 接口注册到一个有点全局的ObjectMapper):

@JsonComponent
public class FooDeserializer extends JsonDeserializer<Outer.Foo> {

    @Override
    public Outer.Foo deserialize(JsonParser parser, DeserializationContext context)
            throws IOException {
        JsonNode node = parser.getCodec().readTree(parser);
        if (node.isTextual()) {
            return new Foo(node.asText(), null);
        }
        return <delegate to next applicable deserializer>;
    }

}

我无法弄清楚如何执行“委托给下一个适用的反序列化器”部分,因为我尝试过的每个解决方案(例如 parser.getCodec().treeToValue(node, Outer.Foo.class))最终都会再次使用相同的自定义反序列化器,从而导致无限递归。这甚至可能吗?

【问题讨论】:

    标签: java json spring-boot jackson json-deserialization


    【解决方案1】:

    感谢 schummar 的回答:How do I call the default deserializer from a custom deserializer in Jackson。根据上述答案,
    1. @JsonComponent注解应该从自定义序列化器中移除,因为我们需要使用默认序列化器构造自定义序列化器,而@JsonComponent不支持。
    2. 使用BeanDeserializerModifierSimpleModule 注册到ObjectMapper,并使用我们使用默认序列化程序构造的自定义序列化程序修改序列化程序。
    3.在自定义序列化器的serialize方法中,处理特殊情况,正常情况下将序列化委托给默认序列化器。

    以下代码演示了如何实现以上几点。

    主类

    import java.io.IOException;
    
    import com.fasterxml.jackson.core.JsonParseException;
    import com.fasterxml.jackson.databind.BeanDescription;
    import com.fasterxml.jackson.databind.DeserializationConfig;
    import com.fasterxml.jackson.databind.JsonDeserializer;
    import com.fasterxml.jackson.databind.JsonMappingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
    import com.fasterxml.jackson.databind.module.SimpleModule;
    
    public class DelegateDeserializer {
        public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
    
            ObjectMapper mapper = new ObjectMapper();
    
            SimpleModule simpleModule = new SimpleModule();
    
            simpleModule.setDeserializerModifier(new BeanDeserializerModifier() {
                @Override
                public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc,
                        JsonDeserializer<?> deserializer) {
                    if (Outer.Foo.class.isAssignableFrom(beanDesc.getBeanClass())) {
                        return new FooDeserializer(deserializer, beanDesc.getBeanClass());
                    }
                    return deserializer;
                }
            });
    
            mapper.registerModule(simpleModule);
    
            Outer outer1 = mapper.readValue(getType1Json(), Outer.class);
            Outer outer2 = mapper.readValue(getType2Json(), Outer.class);
            System.out.println("deserialize json with object structure:");
            System.out.println(outer1.getFoo().getName());
            System.out.println(outer1.getFoo().getBar());
            System.out.println("deserialize json with string field only:");
            System.out.println(outer2.getFoo().getName());
            System.out.println(outer2.getFoo().getBar());
        }
    
        private static String getType1Json() {
    
            return "  {                                                                        "
                    + "  \"foo\": {                                                            "
                    + "     \"name\": \"John Smith\",                                          "
                    + "    \"bar\": \"baz\"                                                    "
                    + "   }                                                                    "
                    + "}                                                                       ";
    
        }
    
        private static String getType2Json() {
    
            return "  {                                                                        "
                    + "  \"foo\": \"John Smith\"                                               "
                    + "}                                                                       ";
    
        }
    }
    

    FooDeserializer 类

    import java.io.IOException;
    import com.fasterxml.jackson.core.JsonParser;
    import com.fasterxml.jackson.core.JsonToken;
    import com.fasterxml.jackson.databind.DeserializationContext;
    import com.fasterxml.jackson.databind.JsonDeserializer;
    import com.fasterxml.jackson.databind.JsonMappingException;
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.deser.ResolvableDeserializer;
    import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
    
    import jackson.Outer.Foo;
    
    public class FooDeserializer extends StdDeserializer<Outer.Foo> implements ResolvableDeserializer {
    
        private static final long serialVersionUID = 1L;
        private final JsonDeserializer<?> defaultDeserializer;
    
        public FooDeserializer(JsonDeserializer<?> defaultDeserializer, Class<?> clazz) {
            super(clazz);
            this.defaultDeserializer = defaultDeserializer;
        }
    
        @Override
        public Outer.Foo deserialize(JsonParser parser, DeserializationContext context) throws IOException {
            if (parser.getCurrentToken() == JsonToken.VALUE_STRING) {
                JsonNode node = parser.getCodec().readTree(parser);
                if (node.isTextual()) {
                    return new Foo(node.asText(), null);
                }
            }
    
            return (Foo) defaultDeserializer.deserialize(parser, context);
        }
    
        @Override
        public void resolve(DeserializationContext ctxt) throws JsonMappingException {
            ((ResolvableDeserializer) defaultDeserializer).resolve(ctxt);
        }
    
    }
    

    外部类

     public class Outer {
        private Foo foo;
    
        public Foo getFoo() {
            return foo;
        }
    
        public void setFoo(Foo foo) {
            this.foo = foo;
        }
    
        public static class Foo {
            private String bar;
            private String name;
    
            public String getName() {
                return name;
            }
    
            public void setName(String name) {
                this.name = name;
            }
    
            public String getBar() {
                return bar;
            }
    
            public void setBar(String bar) {
                this.bar = bar;
            }
    
            public Foo() {
            }
    
            public Foo(String name, String bar) {
                this.name = name;
                this.bar = bar;
            }
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 2021-12-27
      • 2018-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-06
      • 1970-01-01
      相关资源
      最近更新 更多