且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何在Jackson JSON(反序列化)中自定义序列化或转换具有自定义键类型的Map属性?

更新时间:2023-01-16 08:40:49

这里的问题是,当您使用一样被序列化.

The problem here is that when you use Map.Entry the key has to be a string, because it gets serialized like {"key": value}.

您有两个选择

您的第一个选择,如果您可以将对象序列化为字符串,则可以将其用作json键.

Your first option if you can serialize your object as string you can use it as the json key.

在两种情况下,当对象包含单个字段时(例如您的示例中的一个字段),这是可能的.例如

This is posible in two cases, when the object contains a single field (like the one in your example). e.g.

new SingleFieldObject(2l) // can be serialized as "2"

或者包含多个可以表示为字符串的字段时.例如

Or when constains multiple fields that can be represented as string. e.g.

new MultipleFieldObject("John", 23) // can be serialized as "John 23 Years Old"

现在,可以将自定义对象表示为字符串,您可以使用映射或条目列表.

Now that the custom object can be represented as string you could use either a map or a list of entries.

要使用简单的地图,只需在注释中使用属性"keyUsing",而且还必须定义自定义序列化器和反序列化器.

To use a simple map just use the attribute 'keyUsing' in the annotations, and also you have to define the custom serializer and deserializer.

public class MyKeyDeserializer extends KeyDeserializer {
    @Override
    public Entity2 deserializeKey(String key,
                                  DeserializationContext ctxt) throws IOException {
        return new Entity2(Long.parseLong(key));
    }
}

public class MyKeySerializer extends JsonSerializer<Entity2> {
    @Override
    public void serialize(Entity2 value,
                          JsonGenerator gen,
                          SerializerProvider serializers) throws IOException {
        gen.writeFieldName(value.getId().toString());
    }
}

然后,使用序列化器和反序列化器注释该字段:

Then you annotate the field with your serializer and deserializer:

@JsonSerialize(keyUsing = MyKeySerializer.class) // no need of converter
@JsonDeserialize(keyUsing = MyKeyDeserializer.class) // no need of converter
private Map<Entity2, Integer> valueMap = new HashMap<>();

使用此对象.

Entity1 entity1 = new Entity1(1l);
Entity2 entity2_1 = new Entity2(2l);
Entity2 entity2_2 = new Entity2(3l);
entity1.getValueMap().put(entity2_1, 21);
entity1.getValueMap().put(entity2_2, 22);

会生成这样的JSON

A JSON like this is generated

{
    "id": 1,
    "valueMap": {
        "2": 21,
        "3": 22
    }
}


要使用列表,您可以在示例中使用转换器,但改为使用Entity2来返回键的字符串.


To use a list you could use the converters in your example, but instead Entity2 you return a String for the key.

public class ValueMapListConverter 
    extends StdConverter<Map<Entity2, Integer>, List<Entry<String, Integer>>> {
    @Override
    public List<Entry<String, Integer>> convert(Map<Entity2, Integer> value) {
        List<Entry<String, Integer>> result = new ArrayList<>();
        for (Entry<Entity2, Integer> entry : value.entrySet()) {
            result.add(new SimpleEntry<>(entry.getKey().getId().toString(), 
                       entry.getValue()));
        }
        return result;
    }
}

public class ValueMapMapConverter 
    extends StdConverter<List<Entry<String, Integer>>, Map<Entity2, Integer>> {
    @Override
    public Map<Entity2, Integer> convert(List<Entry<String, Integer>> value) {
        Map<Entity2, Integer> retValue = new HashMap<>();
        for(Entry<String, Integer> entry : value) {
            retValue.put(new Entity2(Long.parseLong(entry.getKey())), entry.getValue());
        }
        return retValue;
    }
}

会生成这样的JSON

A JSON like this is generated

{
    "id": 1,
    "valueMap": [
        { "2": 21 },
        { "3": 22 }
    ]
}

在两种情况下,值Integer可能是一个复杂的对象.

In both cases the value Integer could be a complex object.

您的第二个选项是使用自定义对象,同样,您有多个选项,一个对象包含键的所有字段和值的一个或多个字段.

Your second option is to use a custom object, again you have multiple options, one object that hold all the fields of the key and the field/fields of the value.

// ... serialization - deserialization of the object
public class CustomObject {
    private Long id; // ... all key fields
    private int value; // ... all value fields
}

然后使用转换器 public class ValueListMapConverter extends StdConverter<List<CustomObject>, Map<Entity2, Integer>>public class ValueMapMapConverter extends StdConverter<Map<Entity2, Integer>, List<CustomObject>>

这会生成这样的JSON

This generates a JSON like this

{
    "id": 1,
    "valueMap": [
        { "id": 2, "value": 21 },
        { "id": 3, "value": 22 }
    ]
}


您可以使用地图代替列表并使用键,键对象的其余字段以及自定义对象中的值字段.


You could use a map instead a list and use a key, and the rest of the fields of the key object, together with the value fields in a custom object.

// ... serialization - deserialization of the object
public class CustomObject {
    // ... rest of the key fields
    private int value; // ... all value fields
}

转换器 public class ValueListMapConverter extends StdConverter<Map<Long, CustomObject>, Map<Entity2, Integer>>public class ValueMapMapConverter extends StdConverter<Map<Entity2, Integer>, Map<Long, CustomObject>>

这会生成这样的JSON

This generates a JSON like this

{
    "id": 1,
    "valueMap": {
        "2": { "value": 21 },
        "3": { "value": 22 },
    }
}