简体   繁体   English

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

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

I would like to serialize a given hashmap into json and deserialize it back to the original map.我想将给定的哈希映射序列化为 json 并将其反序列化回原始映射。

Here I would like to make this generic so that it behaves seamlessly regardless of the type of the value.在这里,我想使这个通用,以便无论值的类型如何,它都可以无缝运行。

I am using the following code snippet for constructing the map and then serailizing it as 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);

This is working fine when the type of value that is going to put into the map is an object but fails while the value is of any primitive type ie String/Integer etc.当要放入映射中的值的类型是一个对象但该值是任何原始类型(例如字符串/整数等)时失败,这工作正常。

I can handle this by writing a check if the associated value type is of any primitive type and add if else accordingly.我可以通过编写检查关联的值类型是否属于任何原始类型并相应地添加 if else 来处理此问题。

But I would like to know if there is any better way to do this.但我想知道是否有更好的方法来做到这一点。 Thanks.谢谢。

In JSON specification recognized values are: JSON Object - {...} , JSON Array - [...] , string , number , false , true and null .JSON规范中,公认的值是: JSON Object - {...}JSON Array - [...]stringnumberfalsetruenull Only JSON Object can be deserialised by default to Map and Map can be serialised to a JSON Object .默认情况下,只有JSON Object可以反序列化为Map并且Map可以序列化为JSON Object

In your case you need to handle other types manually and convert them to Map instance somehow.在您的情况下,您需要手动处理其他类型并以某种方式将它们转换为Map实例。 You can implement your own com.fasterxml.jackson.databind.deser.DeserializationProblemHandler which allows to intercept conversion mechanism in case of problems.您可以实现自己的com.fasterxml.jackson.databind.deser.DeserializationProblemHandler ,它允许在出现问题时拦截转换机制。

Below you can find a simple implementation how to do that:您可以在下面找到一个简单的实现方法:

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 + '\'' +
                '}';
    }
}

Above code prints:上面的代码打印:

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}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM