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

huangapple go评论72阅读模式
英文:

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)); // <----当参数值为基本类型时,此行失败。
}

return mapper.writeValueAsString(argumentNameValueMap);

当要放入映射中的值的类型为对象时,这段代码正常工作,但在值的类型为任何基本类型(如String/Integer等)时会失败。

我可以通过检查关联值的类型是否为任何基本类型,并相应地添加 if else 条件来处理这个问题。

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

英文:

I would like to serialize a given hashmap into json and deserialize it back to the original map.

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:

Map&lt;String, Map&lt;String, Object&gt;&gt; argumentNameValueMap = new HashMap&lt;&gt;();

for (int i = 0; i &lt; codeSignature.getParameterNames().length; i++) {
  argumentNameValueMap.put(codeSignature.getParameterNames()[i],
      mapper.convertValue(joinPoint.getArgs()[i],
          Map.class)); &lt;----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 i.e. 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.

But I would like to know if there is any better way to do this. Thanks.

答案1

得分: 1

`JSON`规范中被认可的值包括:`JSON Object` - `{...}`,`JSON Array` - `[...]`,`string`,`number`,`false`,`true``null`。只有`JSON Object`可以默认被反序列化为`Map`,`Map`可以被序列化为`JSON Object`。

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

以下是一个简单的实现示例

```java
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}
英文:

In JSON specification recognized values are: JSON Object - {...}, JSON Array - [...], string, number, false, true and null. Only JSON Object can be deserialised by default to Map and Map can be serialised to a JSON Object.

In your case you need to handle other types manually and convert them to Map instance somehow. You can implement your own com.fasterxml.jackson.databind.deser.DeserializationProblemHandler which allows to intercept conversion mechanism in case of problems.

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&lt;String, Object&gt; source = new HashMap&lt;&gt;();
source.put(&quot;pojo&quot;, new SomeClass());
source.put(&quot;string&quot;, &quot;String-Value&quot;);
source.put(&quot;int&quot;, 1);
source.put(&quot;null&quot;, null);
source.put(&quot;char&quot;, &#39;A&#39;);
source.put(&quot;long&quot;, Long.MIN_VALUE);
source.put(&quot;list&quot;, Arrays.asList(1, 3));
source.put(&quot;array&quot;, new int[]{12, 13});
ObjectMapper mapper = new ObjectMapper();
mapper.addHandler(new Convert2MapDeserializationProblemHandler());
Map&lt;String, Map&lt;String, Object&gt;&gt; argumentNameValueMap = new HashMap&lt;&gt;();
for (Map.Entry&lt;String, Object&gt; entry : source.entrySet()) {
argumentNameValueMap.put(entry.getKey(), mapper.convertValue(entry.getValue(), Map.class));
}
argumentNameValueMap.forEach((k, v) -&gt; System.out.println(k + &quot; -&gt; &quot; + v));
}
}
class Convert2MapDeserializationProblemHandler extends DeserializationProblemHandler {
@Override
public Object handleMissingInstantiator(DeserializationContext ctxt, Class&lt;?&gt; instClass, ValueInstantiator valueInsta, JsonParser p, String msg) throws IOException {
if (Map.class.isAssignableFrom(instClass)) {
Map&lt;String, Object&gt; map = new LinkedHashMap&lt;&gt;();
TreeNode value = p.readValueAsTree();
map.put(&quot;value&quot;, 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&lt;String, Object&gt; map = new LinkedHashMap&lt;&gt;();
TreeNode value = p.readValueAsTree();
map.put(&quot;value&quot;, value);
return map;
}
return super.handleUnexpectedToken(ctxt, targetType, t, p, failureMsg);
}
}
class SomeClass {
String stringField = &quot;Value!&quot;;
public String getStringField() {
return stringField;
}
public void setStringField(String stringField) {
this.stringField = stringField;
}
@Override
public String toString() {
return &quot;SomeClass{&quot; +
&quot;stringField=&#39;&quot; + stringField + &#39;\&#39;&#39; +
&#39;}&#39;;
}
}

Above code prints:

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

huangapple
  • 本文由 发表于 2020年10月14日 17:43:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/64350618.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定