在 Java 中使用 Jackson 将具有不同类型值的映射序列化为 JSON

Serializing map having value of different types to JSON using Jackson in Java

我想将给定的哈希映射序列化为 json 并将其反序列化回原始映射。

在这里,我想使它成为通用的,这样无论值的类型如何,它都能无缝运行。

我正在使用以下代码片段构建地图,然后将其序列化为 json:

Map<String, Map<String, Object>> argumentNameValueMap = new HashMap<>();

for (int i = 0; i < codeSignature.getParameterNames().length; i++) {
  argumentNameValueMap.put(codeSignature.getParameterNames()[i],
      mapper.convertValue(joinPoint.getArgs()[i],
          Map.class)); <----THIS LINE IS FAILING WHEN ARGUMENT VALUE IS OF PRIMITIVE TYPE.
}

return mapper.writeValueAsString(argumentNameValueMap);

当要放入映射中的值类型是一个对象时,这工作正常,但当值是任何原始类型时失败,即 String/Integer 等

我可以通过编写检查关联值类型是否为任何原始类型并相应地添加 if else 来处理此问题。

但是我想知道是否有更好的方法来做到这一点。谢谢

JSON 规范中识别的值为:JSON Object - {...}JSON Array - [...]stringnumberfalsetruenull。默认情况下,只有 JSON Object 可以反序列化为 Map,而 Map 可以序列化为 JSON Object.

在您的情况下,您需要手动处理其他类型并以某种方式将它们转换为 Map 实例。您可以实现自己的 com.fasterxml.jackson.databind.deser.DeserializationProblemHandler,它允许在出现问题时拦截转换机制。

您可以在下面找到一个简单的实现方法:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
import com.fasterxml.jackson.databind.deser.ValueInstantiator;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

public class JsonApp {

    public static void main(String[] args) {
        Map<String, Object> source = new HashMap<>();
        source.put("pojo", new SomeClass());
        source.put("string", "String-Value");
        source.put("int", 1);
        source.put("null", null);
        source.put("char", 'A');
        source.put("long", Long.MIN_VALUE);
        source.put("list", Arrays.asList(1, 3));
        source.put("array", new int[]{12, 13});

        ObjectMapper mapper = new ObjectMapper();
        mapper.addHandler(new Convert2MapDeserializationProblemHandler());

        Map<String, Map<String, Object>> argumentNameValueMap = new HashMap<>();
        for (Map.Entry<String, Object> entry : source.entrySet()) {
            argumentNameValueMap.put(entry.getKey(), mapper.convertValue(entry.getValue(), Map.class));
        }
        argumentNameValueMap.forEach((k, v) -> System.out.println(k + " -> " + v));
    }
}

class Convert2MapDeserializationProblemHandler extends DeserializationProblemHandler {
    @Override
    public Object handleMissingInstantiator(DeserializationContext ctxt, Class<?> instClass, ValueInstantiator valueInsta, JsonParser p, String msg) throws IOException {
        if (Map.class.isAssignableFrom(instClass)) {
            Map<String, Object> map = new LinkedHashMap<>();
            TreeNode value = p.readValueAsTree();
            map.put("value", value);
            return map;
        }
        return super.handleMissingInstantiator(ctxt, instClass, valueInsta, p, msg);
    }

    @Override
    public Object handleUnexpectedToken(DeserializationContext ctxt, JavaType targetType, JsonToken t, JsonParser p, String failureMsg) throws IOException {
        if (Map.class.isAssignableFrom(targetType.getRawClass())) {
            Map<String, Object> map = new LinkedHashMap<>();
            TreeNode value = p.readValueAsTree();
            map.put("value", value);
            return map;
        }
        return super.handleUnexpectedToken(ctxt, targetType, t, p, failureMsg);
    }
}

class SomeClass {
    String stringField = "Value!";

    public String getStringField() {
        return stringField;
    }

    public void setStringField(String stringField) {
        this.stringField = stringField;
    }

    @Override
    public String toString() {
        return "SomeClass{" +
                "stringField='" + stringField + '\'' +
                '}';
    }
}

以上代码打印:

pojo -> {stringField=Value!}
string -> {value="String-Value"}
null -> null
array -> {value=[12,13]}
char -> {value="A"}
list -> {value=[1,3]}
int -> {value=1}
long -> {value=-9223372036854775808}