简体   繁体   English

在地图上未经检查的投射<String, Object> (JSON 转换为 Map with Jackson)

[英]Unchecked cast on a Map<String, Object> (JSON converted to Map with Jackson)

In Java 8, I want to convert a JSON string to a map, and apply "complex" transformations to the keys.在 Java 8 中,我想将 JSON 字符串转换为映射,并对键应用“复杂”转换。 As an example , that "complex" transformation will simply be a lower case transformation.例如,“复杂”转换将只是一个小写转换。 Values in the input JSON could be strings or nested JSON objects.输入 JSON 中的值可以是字符串或嵌套的 JSON 对象。 My code is actually working, but I struggle to fix an unchecked cast warning.我的代码实际上正在运行,但我很难修复unchecked cast警告。

Example例子

Example JSON input ( String ) : JSON 输入示例( String ):

{
    "Key1": "value1",
    "Key2": {
        "Key2.1": "value2.1"
    }
}

Desired output ( Map ) :所需的输出( Map ):

"key1" -> "value1"
"key2" ->
    "key2.1" -> "value2.1"

1. JSON String -> Map 1. JSON 字符串 -> 地图

For that part, I used Jackson (2.9.8) and defined the following function :对于那部分,我使用了 Jackson (2.9.8) 并定义了以下函数:

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

Map<String, Object> convertJsonStringToMap(String json) throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    TypeReference type = new TypeReference<Map<String, Object>>(){};
    return mapper.readValue(json, type);
}

Since values can be strings or JSON objects, the return type of that function is Map<String, Object> .由于值可以是字符串或 JSON 对象,因此该函数的返回类型是Map<String, Object> Note also that readValue (in ObjectMapper class) uses generics, its signature is :还要注意readValue (在ObjectMapper类中)使用泛型,它的签名是:

<T> T readValue(String content, TypeReference valueTypeRef)

2. Map -> Map with transformed keys (example : lower case) 2. 映射 -> 带有转换键的映射(例如:小写)

I defined the following function :我定义了以下函数:

Map<String, Object> transformKeys(Map<String, Object> map) {
    Map<String, Object> result = new HashMap<>(map.size());

    for (Map.Entry<String, Object> entry : map.entrySet()) {
        Object value = entry.getValue();
        if (value instanceof Map) {
            value = transformKeys((Map<String, Object>) value);
        }
        // toLowerCase() is the transformation here (example), but I could have used something else
        result.put(entry.getKey().toLowerCase(), value);
    }

    return result;
}

To handle nested maps, this function is recursive.为了处理嵌套映射,这个函数是递归的。 But since it takes a Map<String, Object> as parameter, I must cast value to Map<String, Object> to call the method recursively.但是由于它需要一个Map<String, Object>作为参数,我必须将valueMap<String, Object>以递归调用该方法。

3. Putting all together 3. 全部放在一起

String json = "{\"Key1\": \"value1\", \"Key2\": { \"Key2.1\": \"value2.1\" }}";
Map<String, Object> initialMap = convertJsonStringToMap(json);
Map transformedMap = transformKeys(initialMap);
System.out.println(transformedMap);

This code works, and prints, as expected :此代码按预期工作并打印:

{key1=value1, key2={key2.1=value2.1}} {key1=value1, key2={key2.1=value2.1}}

But this line in transformKeys function :但是transformKeys函数中的这一行:

value = transformKeys((Map<String, Object>) value);

produces a warning :产生警告:

[WARNING] App.java:[29,74] unchecked cast
    required: java.util.Map<java.lang.String,java.lang.Object>
    found:    java.lang.Object

The warning is clear and I understand it (the compiler cannot know if value is really an instance of Map<String, Object> ), but is there a way to get rid of it?警告很清楚,我理解它(编译器无法知道value是否真的是Map<String, Object>的实例),但是有没有办法摆脱它?

(No @SuppressWarnings please, nor -Xlint:none ) :D (请不要@SuppressWarnings ,也不要-Xlint:none ):D

I feel like returning a Map<String, Object> from convertJsonStringToMap is not the cleanest way to convert JSON String to a Map, but I can't find an other way to do it with Jackson.我觉得从convertJsonStringToMap返回Map<String, Object>并不是将 JSON String 转换为 Map 的最干净的方法,但我找不到其他方法来使用 Jackson。

You need to use Object as Map value because it could be another Map , List or primitive ( String , Integer , etc.).您需要使用Object作为Map值,因为它可能是另一个MapListprimitiveStringInteger等)。 Jackson allows also to manipulate JSON using JsonNode types. Jackson还允许使用JsonNode类型操作JSON We need to traverse JSON object but also JSON array (you forgot about it).我们需要遍历JSON object JSON array (你忘了它)。 In that case we need to:在这种情况下,我们需要:

  • Deserialise JSON to JsonNode .JSON反序列JsonNode
  • Traverse it using JsonNode API .使用JsonNode API遍历它。
  • Convert to the Map转换为Map

Simple implementation:简单的实现:

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        String json = "{\"Key1\": \"value1\", \"Key2\": { \"Key2.1\": \"value2.1\" }, \"Key3\":[{\"pRiMe\":11}]}";
        Map<String, Object> map = convertJsonStringToMap(json);

        System.out.println(map);
    }

    private static Map<String, Object> convertJsonStringToMap(String json) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        JsonNode node = mapper.readTree(json);
        transformKeys(node);

        TypeReference mapType = new TypeReference<Map<String, Object>>() {
        };

        return mapper.convertValue(node, mapType);
    }

    private static void transformKeys(JsonNode parent) {
        if (parent.isObject()) {
            ObjectNode node = (ObjectNode) parent;

            List<String> names = new ArrayList<>();
            node.fieldNames().forEachRemaining(names::add);

            names.forEach(name -> {
                JsonNode item = node.remove(name);
                transformKeys(item);
                node.replace(name.toLowerCase(), item);
            });
        } else if (parent.isArray()) {
            ArrayNode array = (ArrayNode) parent;
            array.elements().forEachRemaining(JsonApp::transformKeys);
        }
    }
}

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

{key1=value1, key2={key2.1=value2.1}, key3=[{prime=11}]}

We got rid of unsafe casting and our implementation is more concise.我们摆脱了不安全的铸造,我们的实现更加简洁。

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

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