jackson按照自定义注解序列化和反序列化属性

  背景

        在springboot-web项目中,绝大多时候都是使用json格式来传输数据,springboot默认使用jackson来转换,一般情况下,默认的转化设置已经可以满足要求。特殊一点的使用Jackson相关的注解辅助也能完成。

        但是在一次开发过程中,要求对某些字段进行加解密,但是由于是spring微服务的项目,所以只在其中一个服务中需要加密,所以没有办法使用@JsonSerialize 和 @JsonDeserialize着两种jackson的注解。

解决思路

        一开始想的是,jackson既然实现了自己的注解,那它为了满足用户的需求,肯定做了兼容让用户根据自己的需要做数据的转换。经过查询资料和源码,发现OjectMapper有一个注入内省注解处理器的方法:


    public ObjectMapper setAnnotationIntrospector(AnnotationIntrospector ai) {
        _serializationConfig = _serializationConfig.with(ai);
        _deserializationConfig = _deserializationConfig.with(ai);
        return this;
    }

    public ObjectMapper setAnnotationIntrospectors(AnnotationIntrospector serializerAI,
            AnnotationIntrospector deserializerAI) {
        _serializationConfig = _serializationConfig.with(serializerAI);
        _deserializationConfig = _deserializationConfig.with(deserializerAI);
        return this;
    }

序列化

ObjectMapper在对象序列化时,会使用到 _serializationConfig这个配置。

protected final void _configAndWriteValue(JsonGenerator g, Object value)
        throws IOException
    { 
        SerializationConfig cfg = getSerializationConfig();   //这个方法会返回 包含了 自定义注解解释类的的 _serializationConfig
        cfg.initialize(g); // since 2.5
        if (cfg.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) {
            _configAndWriteCloseable(g, value, cfg);
            return;
        }
        try {
            // _serializerProvider(cfg) 会构建一个默认的 DefaultSerializerProvider序列化对象
            _serializerProvider(cfg).serializeValue(g, value);
        } catch (Exception e) {
            ClassUtil.closeOnFailAndThrowAsIOE(g, e);
            return;
        }
        g.close();
    }
DefaultSerializerProvider继承了SerializerProvider
public JsonSerializer<Object> findTypedValueSerializer(Class<?> valueType,
            boolean cache, BeanProperty property)
        throws JsonMappingException
    {
        // Two-phase lookups; local non-shared cache, then shared:
        JsonSerializer<Object> ser = _knownSerializers.typedValueSerializer(valueType);
        if (ser != null) {
            return ser;
        }
        // If not, maybe shared map already has it?
        ser = _serializerCache.typedValueSerializer(valueType);
        if (ser != null) {
            return ser;
        }

        // Well, let's just compose from pieces:
        ser = findValueSerializer(valueType, property);  //这里获取对应的序列化实现
        TypeSerializer typeSer = _serializerFactory.createTypeSerializer(_config,
                _config.constructType(valueType));
        if (typeSer != null) {
            typeSer = typeSer.forProperty(property);
            ser = new TypeWrappedSerializer(typeSer, ser);
        }
        if (cache) {
            _serializerCache.addTypedSerializer(valueType, ser);
        }
        return ser;
    }

一直走到方法:

 protected JsonSerializer<Object> _createAndCacheUntypedSerializer(Class<?> rawType)
        throws JsonMappingException
    {
        JavaType fullType = _config.constructType(rawType);
        JsonSerializer<Object> ser;
        try {
            ser = _createUntypedSerializer(fullType);  //创建序列化器
        } catch (IllegalArgumentException iae) {
            // We better only expose checked exceptions, since those
            // are what caller is expected to handle
            ser = null; // doesn't matter but compiler whines otherwise
            reportMappingProblem(iae, ClassUtil.exceptionMessage(iae));
        }

        if (ser != null) {
            // 21-Dec-2015, tatu: Best to cache for both raw and full-type key
            _serializerCache.addAndResolveNonTypedSerializer(rawType, fullType, ser, this);
        }
        return ser;
    }
@Override
    @SuppressWarnings("unchecked")
    public JsonSerializer<Object> createSerializer(SerializerProvider prov,
            JavaType origType)
        throws JsonMappingException
    {
        // Very first thing, let's check if there is explicit serializer annotation:
        final SerializationConfig config = prov.getConfig();
        BeanDescription beanDesc = config.introspect(origType);
        //通过注解来创建序列化器
        JsonSerializer<?> ser = findSerializerFromAnnotation(prov, beanDesc.getClassInfo());
        if (ser != null) {
            return (JsonSerializer<Object>) ser;
        }
       ... 忽略其他
    }

最终:

protected JsonSerializer<Object> findSerializerFromAnnotation(SerializerProvider prov,
            Annotated a)
        throws JsonMappingException
    {
        /**
            prov使用的是之前构建的 SerializationConfig 类,其中包含了 我们自定义的 AnnotationIntrospector
            getAnnotationIntrospector()可以获取到这个自定义的AnnotationIntrospector并调用其方法返回我们自定义的序列化器
        */
        Object serDef = prov.getAnnotationIntrospector().findSerializer(a);
        if (serDef == null) {
            return null;
        }
        JsonSerializer<Object> ser = prov.serializerInstance(a, serDef);
        // One more thing however: may need to also apply a converter:
        return (JsonSerializer<Object>) findConvertingSerializer(prov, a, ser);
    }

这样我们就能使用自定义注解+自定义的序列化器来实现字段的序列化。

反序列化

        反序列化的过程和序列化的过程相似。

protected Object _readMapAndClose(JsonParser p0, JavaType valueType)
        throws IOException
    {
        try (JsonParser p = p0) {
            Object result;
            JsonToken t = _initForReading(p, valueType);
            /**这里构建了 使用 _deserializationConfig的 DeserializationConfig 
                而_deserializationConfig中包含了我们自定义的注解解释器
            */
            final DeserializationConfig cfg = getDeserializationConfig();
            final DeserializationContext ctxt = createDeserializationContext(p, cfg);
            if (t == JsonToken.VALUE_NULL) {
                // Ask JsonDeserializer what 'null value' to use:
                result = _findRootDeserializer(ctxt, valueType).getNullValue(ctxt);
            } else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
                result = null;
            } else {
                //这里开始反序列化数据
                JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, valueType);
                if (cfg.useRootWrapping()) {
                    result = _unwrapAndDeserialize(p, ctxt, cfg, valueType, deser);
                } else {
                    result = deser.deserialize(p, ctxt);
                }
                ctxt.checkUnresolvedObjectId();
            }
            if (cfg.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) {
                _verifyNoTrailingTokens(p, ctxt, valueType);
            }
            return result;
        }
    }

        最终也会执行到创建反序列化器的方法(DeserializationContext)中

protected JsonDeserializer<Object> _createDeserializer(DeserializationContext ctxt,
            DeserializerFactory factory, JavaType type)
        throws JsonMappingException
    {
        final DeserializationConfig config = ctxt.getConfig();

        // First things first: do we need to use abstract type mapping?
        if (type.isAbstract() || type.isMapLikeType() || type.isCollectionLikeType()) {
            type = factory.mapAbstractType(config, type);
        }
        BeanDescription beanDesc = config.introspect(type);
        // Then: does type define explicit deserializer to use, with annotation(s)?
        /**
            这里通过获取注解序列化器
        */
        JsonDeserializer<Object> deser = findDeserializerFromAnnotation(ctxt,
                beanDesc.getClassInfo());
        if (deser != null) {
            return deser;
        }

        //... 忽略其他
    }

创建注解序列化器的方法:

protected JsonDeserializer<Object> findDeserializerFromAnnotation(DeserializationContext ctxt,
            Annotated ann)
        throws JsonMappingException
    {
        /**
            ctxt中包含了我们自定的注解解释器 - _deserializationConfig。
        */
        Object deserDef = ctxt.getAnnotationIntrospector().findDeserializer(ann);
        if (deserDef == null) {
            return null;
        }
        JsonDeserializer<Object> deser = ctxt.deserializerInstance(ann, deserDef);
        // One more thing however: may need to also apply a converter:
        return findConvertingDeserializer(ctxt, ann, deser);
    }

        这样就能得到我们自定义的注解解释器,并在注解解释器中返回我们自定义的字段序列化和反序列实现。

具体实现

        首先我们需要自定义一个注解,以及一个包含了注解的实体类。

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;


@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
}
@Data
public class TestBean {
    private String name;

    private Integer age;

    @TestAnnotation
    private String serise;
}

然后实现自定义的注解解释器、序列化和反序列化实现:

        自定义注解解释器实现 NopAnnotationIntrospector 类,并重写findSerializer()和findDeserializer(),在方法中判断是否包含注解,如果包含则返回我们自定义的序列化和饭序列化实现

public class TestDataAnnotationIntrospector extends NopAnnotationIntrospector {
    @Override
    public Object findSerializer(Annotated am) {
        TestAnnotation annotation = am.getAnnotation(TestAnnotation.class);
        if (annotation != null) {
            return TestDataSerializer.class;
        }

        return null;
    }

    @Override
    public Object findDeserializer(Annotated am) {
        TestAnnotation annotation = am.getAnnotation(TestAnnotation.class);
        if (annotation != null) {
            return TestDataDeserializer.class;
        }

        return null;
    }
}

序列化和反序列化实现

public class TestDataDeserializer extends StdDeserializer<String> {
    private static final long serialVersionUID = 1L;

    public TestDataDeserializer() {
        super(String.class);
    }

    @Override
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        // string
        String s = p.getValueAsString();
        return s + "------我加了结尾";
    }
}



public class TestDataSerializer extends StdSerializer<String> {
    private static final long serialVersionUID = 1L;

    public TestDataSerializer() {
        super(String.class);
    }

    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeString("我加了开头---------" + value);
    }
}

测试代码:

public static void main(String[] args){
        TestBean testBean = new TestBean();
        testBean.setName("这是名称");
        testBean.setAge(100);
        testBean.setSerise("这是被注解的数据啊啊啊啊");

        ObjectMapper objectMapper = new ObjectMapper();

        objectMapper.setAnnotationIntrospector(new TestDataAnnotationIntrospector());

        String json = null;
        try {
            json = objectMapper.writeValueAsString(testBean);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        System.out.println("json化结果:" + json);

        try {
            TestBean testBean1 = objectMapper.readValue(json, TestBean.class);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

输出结果:

序列化:

 json化结果:{"name":"这是名称","age":100,"serise":"我加了开头---------这是被注解的数据啊啊啊啊"}

 反序列化:

 springMVC使用自定义的ObjectMapper

方式1:

通过覆盖:springMvc自带的MappingJackson2HttpMessageConverter类

@Bean
    MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(){
        MappingJackson2HttpMessageConverter converter=new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        objectMapper.setAnnotationIntrospector(new DataAnnotationIntrospector());
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        converter.setObjectMapper(objectMapper);
        return converter;
    }

方式2:

通过覆盖:springMvc自带的ObjectMapper类

    @Bean
    ObjectMapper objectMapper(){
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        objectMapper.setAnnotationIntrospector(new DataAnnotationIntrospector());
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        return objectMapper;
    }

方式3:

@Configuration
public class ExtWebMvcConfig implements WebMvcConfigurer {
    @Bean
    ObjectMapper objectMapper(){
        Object[] objects = {
                SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
                , DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS
                , DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE
                , DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
                , DeserializationFeature.FAIL_ON_INVALID_SUBTYPE
                , SerializationFeature.FAIL_ON_EMPTY_BEANS
                , DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
        };

        Jackson2ObjectMapperFactoryBean objectMapper = new Jackson2ObjectMapperFactoryBean();
        objectMapper.setFeaturesToDisable(objects);
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        objectMapper.setFindModulesViaServiceLoader(true);
        objectMapper.setAnnotationIntrospector(new DataAnnotationIntrospector());
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        
        objectMapper.afterPropertiesSet();
        return objectMapper.getObject();
    }

问题

1.@JsonIgnore失效

        以上按照自定实现内省器的方式自定义注解序列化,在一般情况都没有问题,但是,如果是存在@JsonIgnore注解属性或方法,会导致@JsonIgnore失效甚至会报错。

原因:

        NopAnnotationIntrospector中的hasIgnoreMarker方法(是否有忽略Json的标记)和hasRequiredMarker(是否有必须序列化或反序列化的标记)方法没有做任何的实现,这是不符合实际的。

/**
     * Method called to check whether given property is marked to
     * be ignored. This is used to determine whether to ignore
     * properties, on per-property basis, usually combining
     * annotations from multiple accessors (getters, setters, fields,
     * constructor parameters).
     */
    public boolean hasIgnoreMarker(AnnotatedMember m) { return false; }


    /**
     * Method that can be called to check whether this member has
     * an annotation that suggests whether value for matching property
     * is required or not.
     */
    public Boolean hasRequiredMarker(AnnotatedMember m) { return null; }

解决办法:

        1.我们可以重写这hasIgnoreMarker方法和hasRequiredMarker方法。

        2.我们自定义的内省器不要继承NopAnnotationIntrospector,改为继承 JacksonAnnotationIntrospector。

public class TestDataAnnotationIntrospector extends JacksonAnnotationIntrospector{
    @Override
    public Object findSerializer(Annotated am) {
        TestAnnotation annotation = am.getAnnotation(TestAnnotation.class);
        if (annotation != null) {
            return TestDataSerializer.class;
        }

        return null;
    }

    @Override
    public Object findDeserializer(Annotated am) {
        TestAnnotation annotation = am.getAnnotation(TestAnnotation.class);
        if (annotation != null) {
            return TestDataDeserializer.class;
        }

        return null;
    }
}

 2.@JsonProperty和@JsonIgnore同时存在导致@JsonIgnore失效

        重写findNameForSerialization方法,先执行父类原来的逻辑,如果PropertyName为null或者空,则返回null,这样后面的逻辑就不会给PropertyName赋值了,最后也就不会取消Ignore了。

        

public class TestDataAnnotationIntrospector extends JacksonAnnotationIntrospector {


    @Override
    public PropertyName findNameForSerialization(Annotated a)
    {
        PropertyName propertyName = super.findNameForSerialization(a);
        return propertyName == null || StringUtils.isEmpty(propertyName.getSimpleName()) ? null : propertyName;
    }

    @Override
    public Object findSerializer(Annotated am) {
        TestAnnotation annotation = am.getAnnotation(TestAnnotation.class);
        if (annotation != null) {
            return TestDataSerializer.class;
        }

        return null;
    }

    @Override
    public Object findDeserializer(Annotated am) {
        TestAnnotation annotation = am.getAnnotation(TestAnnotation.class);
        if (annotation != null) {
            return TestDataDeserializer.class;
        }

        return null;
    }
}

  • 23
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Jackson是一个Java库,用于将Java对象序列化为JSON格式和反序列化Java对象。要自定义Jackson反序列化器,可以使用@JsonDeserialize注解并传入自定义反序列化器类,如下所示: ``` @JsonDeserialize(using = MyDeserializer.class) public class MyObject { // class fields and methods } ``` MyDeserializer类需要继承com.fasterxml.jackson.databind.JsonDeserializer<T>,并重写deserialize()方法实现自定义反序列化逻辑。 ``` public class MyDeserializer extends JsonDeserializer<MyObject> { @Override public MyObject deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { // custom deserialization logic } } ``` 注意: 类需要添加 Jackson 依赖 ``` <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.11.3</version> </dependency> ``` ### 回答2: 要自定义Jackson反序列化器,可以按照以下步骤操作: 1. 创建自定义反序列化器类:首先创建一个新的类,实现Jackson提供的`JsonDeserializer`接口。该接口要求实现`deserialize`方法,该方法将负责将JSON数据转换为目标Java对象。 2. 实现`deserialize`方法:在`deserialize`方法中,根据需要的逻辑解析JSON数据并创建目标对象,并将其返回。 3. 注册自定义反序列化器:在使用Jackson的代码中,需要将自定义反序列化器注册到`ObjectMapper`中,以便在反序列化时使用。可以使用`SimpleModule`类创建一个新的模块,并使用`addDeserializer`方法将自定义反序列化器与目标类型关联起来。然后,使用`registerModule`方法将模块注册到`ObjectMapper`中。 4. 使用自定义反序列化器:在需要使用自定义反序列化器的代码中,创建`ObjectMapper`实例,并执行反序列化操作。在反序列化时,Jackson将自动选择正确的反序列化器进行处理。 通过以上步骤,我们可以自定义Jackson反序列化器,并在需要时将其应用于特定的对象类型。这样可以根据自定义的逻辑将JSON数据转换为目标对象,使得反序列化的过程更加灵活和个性化。 ### 回答3: 要自定义 Jackson 反序列化器,你可以按照以下步骤进行操作: 1. 创建一个类,并实现 Jackson 的 `JsonDeserializer` 接口。这个接口要求你实现 `deserialize` 方法,用于自定义对象的反序列化逻辑。 2. 在 `deserialize` 方法中,你可以根据需要读取 JSON 中的属性并进行处理。你可以使用 Jackson 的 `JsonParser` 对象来读取 JSON 数据。 3. 根据 JSON 中的属性,你可以创建一个自定义的对象,并将属性值填充进去。 4. 在自定义对象中,你可以添加任何你需要的验证逻辑,以确保对象的属性是有效的。 5. 最后,返回你自定义的对象。 6. 使用自定义的反序列化器,你可以为 Jackson 提供一个 `ObjectMapper`,并使用 `registerModule` 方法注册你的自定义模块。 以下是一个示例代码,用来自定义一个反序列化器: ```java public class CustomDeserializer extends JsonDeserializer<CustomObject> { @Override public CustomObject deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser); // 从 JSON 中读取属性 String property1 = jsonNode.get("property1").asText(); int property2 = jsonNode.get("property2").asInt(); // 创建自定义对象并填充属性 CustomObject customObject = new CustomObject(); customObject.setProperty1(property1); customObject.setProperty2(property2); // 进行一些验证逻辑 if (customObject.getProperty1() == null || customObject.getProperty1().isEmpty()) { throw new IOException("Invalid value for property1"); } return customObject; } } ``` 使用自定义的反序列化器,你可以在 ObjectMapper 中注册你的自定义模块: ```java ObjectMapper objectMapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addDeserializer(CustomObject.class, new CustomDeserializer()); objectMapper.registerModule(module); CustomObject customObject = objectMapper.readValue(jsonString, CustomObject.class); ``` 通过以上步骤,你就可以成功自定义 Jackson反序列化器,并根据你的需求对对象进行反序列化操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值