有什么方法可以使用 Jackson 将带有 BigDecimal 键的 Map 序列化为 BigDecimal 而不是字符串?

Is there any way to serialize a Map with a BigDecimal key as a BigDecimal and not as string using Jackson?

尝试使用 Jackson 和 属性 ObjectMapper.DefaultTyping.NON_FINAL 序列化 Map 时,键被序列化为字符串,而值被正确序列化为 BigDecimal。当我反序列化它时,这会导致问题,因为它被反序列化为 Map.

我需要将密钥序列化为 BigDecimal 而不是字符串,有什么办法吗? 我正在使用 Jackson-all-1.9。11.jar,但我也尝试使用 jackson-2.12.4 罐子,但出现了同样的情况。

有什么解决办法吗?或者我可以创建一个自定义序列化器,它只覆盖 Map Serializer 以在 BigDecimal 键的情况下由 ObjectMapper 自动使用吗?如果是,如何实现?

下面是代码示例,返回结果。

public static void main(String[] args)
{
   try
   {
      ObjectMapper mapper = new ObjectMapper();
      mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
      Map<BigDecimal, BigDecimal> map = new HashMap<>();
      map.put(BigDecimal.ONE, BigDecimal.ONE);
      map.put(BigDecimal.TEN, BigDecimal.TEN);
      String str = mapper.writeValueAsString(map);
      System.out.println(str);
  }
  catch(Exception e)
  {
      e.printStackTrace();
  }
}

结果如下:

["java.util.HashMap",{"10":["java.math.BigDecimal",10],"1":["java.math.BigDecimal",1]}]

您需要为要在 Map 中用作密钥的每个 class 实现一组自定义密钥序列化器和反序列化器。例如,对于数字,它可能如下所示:

class KeyBigDecimalJsonSerializer extends JsonSerializer<Number> {

    @Override
    public void serialize(Number value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeFieldName(value + "_" + value.getClass().getName());
    }
}

class CustomKeyDeserializer extends KeyDeserializer {

    private final ObjectMapper mapper = new ObjectMapper();

    @Override
    public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException {
        if (key.contains("_")) {
            return deserializeKeyWithClass(key, ctxt);
        }
        return key;
    }

    private Object deserializeKeyWithClass(String key, DeserializationContext ctxt) throws IOException {
        String[] parts = key.split("_");
        try {
            Class<?> aClass = ctxt.findClass(parts[1]);
            return mapper.readValue(parts[0], aClass);
        } catch (ClassNotFoundException e) {
            throw new IOException(e);
        }
    }
}

用法:

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

public class MapKeysApp {

    public static void main(String[] args) throws Exception {
        SimpleModule module = new SimpleModule();
        module.addKeySerializer(Number.class, new KeyBigDecimalJsonSerializer());
        module.addKeyDeserializer(Object.class, new CustomKeyDeserializer());

        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(module);
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        Map<Object, Object> map = new HashMap<>();
        map.put(BigDecimal.ONE, BigDecimal.ONE);
        map.put(BigDecimal.TEN, BigDecimal.TEN);
        map.put(2.1D, 2.1D);
        map.put(100, 100);
        map.put("TEN", "ten");

        String json = mapper.writeValueAsString(map);
        System.out.println(json);
        mapper.readValue(json, Map.class).forEach((k, v) -> {
            System.out.println(k + " (" + k.getClass().getName() + ") : " + v + " (" + v.getClass().getName() + ")");
        });
    }
}

以上代码打印:

["java.util.HashMap",{"100_java.lang.Integer":100,"10_java.math.BigDecimal":["java.math.BigDecimal",10],"TEN":"ten","2.1_java.lang.Double":2.1,"1_java.math.BigDecimal":["java.math.BigDecimal",1]}]
100 (java.lang.Integer) : 100 (java.lang.Integer)
10 (java.math.BigDecimal) : 10 (java.math.BigDecimal)
TEN (java.lang.String) : ten (java.lang.String)
2.1 (java.lang.Double) : 2.1 (java.lang.Double)
1 (java.math.BigDecimal) : 1 (java.math.BigDecimal)