Jackson 自定义序列化和反序列化 [英] Jackson custom serialization and deserialization

查看:98
本文介绍了Jackson 自定义序列化和反序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法找出使用 jackson 实现自定义序列化/反序列化的正确方法.我有很多类(约 50 个)带有应该序列化/反序列化而不是作为原始字段的原始字段.喜欢:

i'm unable to figure out the proper way to implement the custom serialization/deserialization with jackson. I have many classes (~50) with primitive fields that should be serialized/deserialized not as primitives. like:

class User {
    int height // this field should be serialized as "height": "10 m"
}

class Food {
    int temperature // this field should be serialized as "temperature": "50 C"
}

class House {
    int width // this field should be serialized as "width": "10 m"
}

所有的序列化和反序列化都非常相似,我只需要在整数后添加一个后缀(C、页数、米数等.)

all serializations and deserializations are very similar, I just need to add a suffix after the integer (C, pages, meters, etc..)

一个简单的方法是在每个这样的字段上放置一对 @JsonSerialize/@JsonDeserialize 注释并实现它们.但我最终会得到 100 个非常相似的序列化器/反序列化器.

A straightforward way to do this is to put a pair of @JsonSerialize/@JsonDeserialize annotation to each such field and implement them. But i will end up with 100 very similar serializers / deserializers.

我想为每个字段添加自定义注释,比如 @Units("Degree")@Units("Meters"),到这样的整数字段并实现一个 SerializationProvider ,它将基于注释值以通用方式创建序列化程序.但是我没有找到提供属性注释信息的地方.

I thought about adding custom annotation to each field, say @Units("Degree") or @Units("Meters"), to such integer fields and implement a SerializationProvider that will create serializers in a generic way based on an annotation value. But I didn't find a place where the information about the property annotations is available.

推荐答案

带有 Unit 注释的想法真的很好.我们只需要添加自定义的 com.fasterxml.jackson.databind.ser.BeanSerializerModifiercom.fasterxml.jackson.databind.ser.BeanPropertyWriter 实现.让我们首先创建我们的注释类:

Idea with Unit annotation is really good. We need to only add custom com.fasterxml.jackson.databind.ser.BeanSerializerModifier and com.fasterxml.jackson.databind.ser.BeanPropertyWriter implementations. Let's create first our annotation class:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Unit {
    String value();
}

POJO 模型可能如下所示:

class Pojo {

    private User user = new User();
    private Food food = new Food();
    private House house = new House();

    // getters, setters, toString
}

class User {

    @Unit("m")
    private int height = 10;

    // getters, setters, toString
}

class Food {

    @Unit("C")
    private int temperature = 50;

    // getters, setters, toString
}

class House {

    @Unit("m")
    private int width = 10;

    // getters, setters, toString
}

有了所有这些,我们需要自定义属性序列化:

Having all of that we need to customise property serialisation:

class UnitBeanSerializerModifier extends BeanSerializerModifier {

    @Override
    public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
        for (int i = 0; i < beanProperties.size(); ++i) {
            final BeanPropertyWriter writer = beanProperties.get(i);
            AnnotatedMember member = writer.getMember();
            Unit units = member.getAnnotation(Unit.class);
            if (units != null) {
                beanProperties.set(i, new UnitBeanPropertyWriter(writer, units.value()));
            }
        }
        return beanProperties;
    }
}

class UnitBeanPropertyWriter extends BeanPropertyWriter {

    private final String unit;

    protected UnitBeanPropertyWriter(BeanPropertyWriter base, String unit) {
        super(base);
        this.unit = unit;
    }

    @Override
    public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception {
        gen.writeFieldName(_name);
        final Object value = (_accessorMethod == null) ? _field.get(bean) : _accessorMethod.invoke(bean, (Object[]) null);
        gen.writeString(value + " " + unit);
    }
}

使用 SimpleModule 我们可以注册它并与 ObjectMapper 一起使用:

Using SimpleModule we can register it and use with ObjectMapper:

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.List;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        SimpleModule unitModule = new SimpleModule();
        unitModule.setSerializerModifier(new UnitBeanSerializerModifier());

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

        Pojo pojo = new Pojo();
        System.out.println(mapper.writeValueAsString(pojo));
    }
}

印刷品:

{
  "user" : {
    "height" : "10 m"
  },
  "food" : {
    "temperature" : "50 C"
  },
  "house" : {
    "width" : "10 m"
  }
}

当然,您需要对其进行测试并处理所有极端情况,但上面的示例显示了总体思路.以类似的方式,我们可以处理反序列化.我们需要实现自定义的BeanDeserializerModifier 和一个自定义的UnitDeserializer:

Of course, you need to test it and handle all corner cases but above example shows general idea. In the similar way we can handle deserialisation. We need to implement custom BeanDeserializerModifier and one custom UnitDeserialiser:

class UnitBeanDeserializerModifier extends BeanDeserializerModifier {

    @Override
    public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
        JsonDeserializer<?> jsonDeserializer = super.modifyDeserializer(config, beanDesc, deserializer);
        if (jsonDeserializer instanceof StdScalarDeserializer) {
            StdScalarDeserializer scalarDeserializer = (StdScalarDeserializer) jsonDeserializer;
            Class scalarClass = scalarDeserializer.handledType();
            if (int.class == scalarClass) {
                return new UnitIntStdScalarDeserializer(scalarDeserializer);
            }
        }
        return jsonDeserializer;
    }
}

int 的示例反序列化器:

and example deserialiser for int:

class UnitIntStdScalarDeserializer extends StdScalarDeserializer<Integer> {

    private StdScalarDeserializer<Integer> src;

    public UnitIntStdScalarDeserializer(StdScalarDeserializer<Integer> src) {
        super(src);
        this.src = src;
    }

    @Override
    public Integer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        String value = p.getValueAsString();
        String[] parts = value.split("\s+");
        if (parts.length == 2) {
            return Integer.valueOf(parts[0]);
        }
        return src.deserialize(p, ctxt);
    }
}

以上实现只是一个例子,应该针对其他原始类型进行改进.我们可以使用简单的模块以相同的方式注册它.重复使用与序列化相同的内容:

Above implementation is just an example and should be improved for other primitive types. We can register it in the same way using simple module. Reuse the same as for serialisation:

unitModule.setDeserializerModifier(new UnitBeanDeserializerModifier());

这篇关于Jackson 自定义序列化和反序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆