Не проверено приведение к карте <String, Object> (JSON преобразован в карту с Джексоном) - PullRequest
2 голосов
/ 20 марта 2019

В 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 String -> Map

Для этой части я использовал Джексона (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>, я должен привести 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> из convertJsonStringToMap не самый чистый способ конвертировать строку JSON в карту, но я не могу найти другой способ сделать это с Джексоном.

1 Ответ

1 голос
/ 20 марта 2019

Вам необходимо использовать Object в качестве значения Map, поскольку это может быть другое значение Map, List или primitive (String, Integer и т. Д.). Jackson позволяет также манипулировать JSON, используя JsonNode типы. Нам нужно пройти JSON object, а также JSON 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}]}

Мы избавились от небезопасного приведения, и наша реализация более краткая.

...