繁体   English   中英

是否可以在类级别为不同的数据类型配置 Jackson 自定义反序列化器?

[英]Is it possible to configure Jackson custom deserializers at class level for different data types?

我需要反序列化一个长而复杂的 json,我为此编写了一组 java 类来映射数据,并且我不得不为许多不同类型的字段(包括 String、Boolean、BigDecimal 等)编写自定义反序列化器。

我知道我可以使用相应的自定义反序列化器(如下所示)注释 java 类中的所有字段,但是我需要注释所有类中的几乎所有字段。

@JsonDeserialize(using = CustomBooleanJsonDeserializer.class)
private boolean active;

我也知道我可以在 Spring 默认的ObjectMapper注册一个模块(就像这里),但我只想将这些自定义反序列化器用于这些特定类。

@Bean
public Module customDeserializersModule() {
    SimpleModule module = new SimpleModule();
    module.addDeserializer(Boolean.class, new CustomBooleanJsonDeserializer());
    // add other custom deserializers 
    return module;
}

我什至知道我可以在RestController使用自定义ObjectMapper ,但我不想放弃通过@RequestBody进行自动数据绑定的便利性,因为我必须防止其他人在没有必要的自定义反序列化器的情况下使用它。

@RequestMapping(method = RequestMethod.POST, value = "/data")
public ResponseEntity<ServerInfo> register(@RequestBody DataMapper data) {
   // DataMapper is the target POJO class of the json's deserialization
}

简而言之,我在课堂上寻找这样的东西:

@JsonDeserialize(using = CustomStringJsonDeserializer.class, forType = String.class)
@JsonDeserialize(using = CustomBooleanJsonDeserializer.class, forType = Boolean.class)
@JsonDeserialize(using = CustomBigDecimalJsonDeserializer.class, forType = BigDecimal.class)
public class DataMapper implements Serializable {
    // obviously, @JsonDeserialize doesn't have a forType method
}

或者可能DataMapper实现自定义反序列化器的某种方法,它定义了如何根据其数据类型反序列化每个字段(无需注释每个字段):

@JsonDeserialize(using = DataMapperJsonDeserializer.class)
public class DataMapper implements Serializable {
    // How can I implement the DataMapperJsonDeserializer with these 
    // characteristics? I know about the ContextualDeserializer interface, 
    // but I don't know how to use it without annotating each field.
}

将模块的效果限制为一个包或一组类的某种方式:

module.restrictedTo(/*some package or set of classes*/);
// com.fasterxml.jackson.databind.Module doesn't have a restrictedTo method

您可以尝试将SimpleModuleContextualDeserializer接口一起使用。 第一个可用于包装默认反序列化器,第二个用于检查类型配置 - 检查注释。

让我们从注解开始:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface ForceCustomDeserializer {
}

我假设您对于给定类型只有一个自定义实现,但如果它不是真正的扩展到注释之上,并提供一些允许使用适当的反序列化器的额外信息。 例如,下面我们可以看到两个自定义反序列化器,它们额外记录一些信息并运行默认反序列化。 使用基本解串器是因为如果您有一些额外的配置,我们不会丢失它。

class CustomBoolDeserializer extends StdScalarDeserializer<Boolean> implements ContextualDeserializer {

    private NumberDeserializers.BooleanDeserializer base;

    public CustomBoolDeserializer(NumberDeserializers.BooleanDeserializer base) {
        super(Boolean.class);
        this.base = base;
    }

    @Override
    public Boolean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        System.out.println("Custom BooleanDeserializer ....");

        return base.deserialize(p, ctxt);
    }

    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) {
        Class<?> parent = property.getMember().getDeclaringClass();
        ForceCustomDeserializer annotation = parent.getAnnotation(ForceCustomDeserializer.class);

        return annotation == null ? base : this;
    }
}

class CustomStringDeserializer extends StringDeserializer implements ContextualDeserializer {

    private final StringDeserializer base;

    public CustomStringDeserializer(StringDeserializer base) {
        this.base = base;
    }

    @Override
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        System.out.println("Custom StringDeserializer ....");

        return base.deserialize(p, ctxt);
    }

    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) {
        Class<?> parent = property.getMember().getDeclaringClass();
        ForceCustomDeserializer annotation = parent.getAnnotation(ForceCustomDeserializer.class);

        return annotation == null ? base : this;
    }
}

我们可以测试上面的自定义实现,如下所示:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.deser.std.NumberDeserializers;
import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
import com.fasterxml.jackson.databind.deser.std.StringDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        SimpleModule forcedCustomModule = new SimpleModule();
        forcedCustomModule.setDeserializerModifier(new BeanDeserializerModifier() {
            @Override
            public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
                if (deserializer instanceof StringDeserializer) {
                    // wrap with yours or return new deserializer
                    return new CustomStringDeserializer((StringDeserializer) deserializer);
                }
                if (deserializer instanceof NumberDeserializers.BooleanDeserializer) {
                    // wrap with yours or return new deserializer
                    return new CustomBoolDeserializer((NumberDeserializers.BooleanDeserializer) deserializer);
                }
                // override for other types

                return deserializer;
            }
        });

        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(forcedCustomModule);

        System.out.println(mapper.readValue(jsonFile, Pojo.class));
    }
}

@ForceCustomDeserializer
class Pojo {

    private String name;
    private boolean bool;

    // getters, setters, toString
}

以下JSON有效负载的示例:

{
  "name": "Jackson",
  "bool": true
}

印刷:

Custom StringDeserializer ....
Custom BooleanDeserializer ....
Pojo{name='Jackson', bool=true}

也可以看看:

您可以为该类定义一个自定义反序列化器(作为问题中的第二个想法)并在其中使用您自己的自定义ObjectMapper

public class DataMapperJsonDeserializer extends JsonDeserializer<DataMapper> {

    private static final ObjectMapper objectMapper = new ObjectMapper();
    private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy");

    static {
        SimpleModule module = new SimpleModule();
        module.addDeserializer(BigInteger.class, new CustomBigIntegerJsonDeserializer());
        module.addDeserializer(BigDecimal.class, new CustomBigDecimalJsonDeserializer());
        module.addDeserializer(Boolean.class, new CustomBooleanJsonDeserializer());
        module.addDeserializer(String.class, new CustomStringJsonDeserializer());
        objectMapper.registerModule(module);
        objectMapper.addMixIn(DataMapper.class, DefaultJsonDeserializer.class);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.setDateFormat(simpleDateFormat);
    }

    @Override
    public DataMapper deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        return objectMapper.readValue(jsonParser, DataMapper.class);
    }

    @JsonDeserialize
    private interface DefaultJsonDeserializer {
        // Reset default json deserializer
    }

}

注意使用的杰克逊混合-在注解所述DefaultJsonDeserializer接口)动态地从删除自定义解串器POJO类,避免了StackOverflowError ,否则将被抛出的结果objectMapper.readValue(jsonParser, DataMapper.class)


那么,就只是对POJO类进行注释:

@JsonDeserialize(using = DataMapperJsonDeserializer.class)
public class DataMapper implements Serializable {
    // It is not necessary to annotate each field with custom deserializers.
}

您甚至可以添加其他POJO类作为DataMapper字段,并且每种类型的自定义反序列化器将自动应用于其字段,无需注释。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM