3

在 Java 8 中,我想将 JSON 字符串转换为映射,并对键应用“复杂”转换。例如,“复杂”转换将只是小写转换。输入 JSON 中的值可以是字符串或嵌套的 JSON 对象。我的代码实际上正在运行,但我很难修复unchecked cast警告。

例子

示例 JSON 输入 ( String) :

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

所需输出 ( Map) :

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

1. JSON 字符串 -> 映射

对于那部分,我使用了 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);
}

由于值可以是字符串或 JSON 对象,因此该函数的返回类型是Map<String, Object>. 另请注意, readValue(在ObjectMapper类中)使用泛型,其签名是:

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

2. 映射 -> 带有转换键的映射(例如:小写)

我定义了以下功能:

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;
}

为了处理嵌套地图,这个函数是递归的。但由于它需要一个Map<String, Object>as 参数,我必须强制value转换Map<String, Object>为递归调用该方法。

3. 将所有内容放在一起

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

此代码按预期工作并打印:

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

但是函数中的这一行transformKeys

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

产生警告:

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

警告很清楚,我理解(编译器不知道是否value真的是 的实例Map<String, Object>),但是有没有办法摆脱它?

@SuppressWarnings请不要,也不要-Xlint:none):D

我觉得返回Map<String, Object>fromconvertJsonStringToMap不是将 JSON String 转换为 Map 的最干净的方法,但我找不到其他方法来处理 Jackson。

4

1 回答 1

2

您需要使用Objectas值,Map因为它可能是另一个或( Map,等)。还允许使用类型进行操作。我们还需要遍历(你忘了)。在这种情况下,我们需要:ListprimitiveStringIntegerJacksonJSONJsonNodeJSON objectJSON array

  • 反序列JSON化为JsonNode.
  • 使用JsonNode API.
  • 转换为Map

简单的实现:

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);
        }
    }
}

上面的代码打印:

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

我们摆脱了不安全的强制转换,我们的实现更加简洁。

于 2019-03-20T19:59:19.607 回答