Как преобразовать строку JSON в карту <String, String> с помощью Jackson JSON

185

Я пытаюсь сделать что-то вроде этого, но это не работает:

Map<String, String> propertyMap = new HashMap<String, String>();

propertyMap = JacksonUtils.fromJSON(properties, Map.class);

Но IDE говорит:

Непроверенное назначение Map to Map<String,String>

Какой правильный способ сделать это? Я использую только Джексона, потому что это то, что уже доступно в проекте, есть ли собственный Java-способ преобразования в / из JSON?

В PHP я бы просто json_decode($str)и вернулся бы массив. Мне нужно в основном то же самое здесь.

adamJLev
источник
Откуда взялся класс JacksonUtils? Я не вижу этого ни в одном из релизов Джексона.
Роб Хайзер
Это наша оболочка для Джексона, она обрабатывает некоторые вещи JsonFactory и ObjectMapper, которые вам нужно сделать.
adamJLev
1
Таким образом, проблема в том, что JacksonUtils.fromJSON () не объявлен как возвращающий Map <String, String>, а просто Map.
Роб Хайзер
7
Кстати, не назначайте новую HashMap там в первой строке: это игнорируется. Просто отвечаю на звонок.
StaxMan
Заголовок не имеет никакого отношения к описанной вами проблеме, которая имеет отношение к нетипизированной коллекции. Ответ ниже является правильным ответом на то, что вы действительно пытались спросить.
Юкка Далбом

Ответы:

313

У меня есть следующий код:

public void testJackson() throws IOException {  
    ObjectMapper mapper = new ObjectMapper(); 
    File from = new File("albumnList.txt"); 
    TypeReference<HashMap<String,Object>> typeRef 
            = new TypeReference<HashMap<String,Object>>() {};

    HashMap<String,Object> o = mapper.readValue(from, typeRef); 
    System.out.println("Got " + o); 
}   

Он читает из файла, но mapper.readValue()также примет InputStreamи вы можете получить InputStreamиз строки с помощью следующего:

new ByteArrayInputStream(astring.getBytes("UTF-8")); 

Есть немного больше объяснений о картографе в моем блоге .

DJNA
источник
2
@Suraj, это согласно документации, и я согласен, что не смог бы вывести формулировку из первых принципов. Это не так странно, как показывать, что Java сложнее, чем мы думаем.
Дня
1
Криг: Я думал, что трудность в том, чтобы начать работу с картографом, но я добавил примечание о том, как применить технику к струне
djna
2
Небольшой комментарий: первая строка создания JsonFactoryне нужна. ObjectMapperможет создать его автоматически самостоятельно.
StaxMan
1
@djna постер попросил, Map<String, String>и вы предоставили Map<String, Object>.
anon58192932
Чтобы написать карту в виде строки, вы можете сделатьmapper.writeValueAsString(hashmap)
Захир
53

Попробуй TypeFactory. Вот код для Джексона JSON (2.8.4).

Map<String, String> result;
ObjectMapper mapper;
TypeFactory factory;
MapType type;

factory = TypeFactory.defaultInstance();
type    = factory.constructMapType(HashMap.class, String.class, String.class);
mapper  = new ObjectMapper();
result  = mapper.readValue(data, type);

Вот код для старой версии Jackson JSON.

Map<String, String> result = new ObjectMapper().readValue(
    data, TypeFactory.mapType(HashMap.class, String.class, String.class));
Нин Сан
источник
38
TypeFactory.mapType (...) устарела, попробуйте следующее: new TypeReference <HashMap <String, String >> () {}
кибер-монах
2
@ cyber-monk Это избавляет от предупреждений, но фактически не проверяет типы.
Дэвид
26

Предупреждение, которое вы получаете, делается компилятором, а не библиотекой (или служебным методом).

Простейший способ использования Джексона напрямую:

HashMap<String,Object> props;

// src is a File, InputStream, String or such
props = new ObjectMapper().readValue(src, new TypeReference<HashMap<String,Object>>() {});
// or:
props = (HashMap<String,Object>) new ObjectMapper().readValue(src, HashMap.class);
// or even just:
@SuppressWarnings("unchecked") // suppresses typed/untype mismatch warnings, which is harmless
props = new ObjectMapper().readValue(src, HashMap.class);

Служебный метод, который вы вызываете, вероятно, просто делает что-то похожее на это.

StaxMan
источник
ОП попросил, Map<String, String>и вы предоставили Map<String, Object>.
anon58192932
18
ObjectReader reader = new ObjectMapper().readerFor(Map.class);

Map<String, String> map = reader.readValue("{\"foo\":\"val\"}");

Обратите внимание, что readerэкземпляр является потокобезопасным.

dpetruha
источник
@dpetruha OP попросил, Map<String, String>и вы предоставили Map<String, Object>.
anon58192932
10

Преобразование из String в JSON Map:

Map<String,String> map = new HashMap<String,String>();

ObjectMapper mapper = new ObjectMapper();

map = mapper.readValue(string, HashMap.class);
Раджа
источник
4
Выше все еще приводит к Type safety: The expression of type HashMap needs unchecked conversion to conform to Map<String,String>. Хотя это можно подавить @SuppressWarningsаннотацией, я бы порекомендовал использовать TypeReferenceпервое или приведение к следующему, как упомянуто в Staxman
Karthic Raghupathi,
1
Чтобы избавиться от предупреждения о безопасности типа, вы можете использовать map = mapper.readValue(string, map.getClass());- учитывая, что вы создали экземпляр карты, как здесь.
MJV
если тип var это Map <Integer, String>, просто получить объект класса не правильно.
Цзяю Ван
5
JavaType javaType = objectMapper.getTypeFactory().constructParameterizedType(Map.class, Key.class, Value.class);
Map<Key, Value> map=objectMapper.readValue(jsonStr, javaType);

Я думаю, что это решит вашу проблему.

JackieZhi
источник
1
в java doc: @since 2.5 - но, вероятно, будет устаревшим в 2.7 или 2.8 (не требуется в 2.7)
Аль-Мотафар
3

Следующие работы для меня:

Map<String, String> propertyMap = getJsonAsMap(json);

где getJsonAsMapопределяется так:

public HashMap<String, String> getJsonAsMap(String json)
{
    try
    {
        ObjectMapper mapper = new ObjectMapper();
        TypeReference<Map<String,String>> typeRef = new TypeReference<Map<String,String>>() {};
        HashMap<String, String> result = mapper.readValue(json, typeRef);

        return result;
    }
    catch (Exception e)
    {
        throw new RuntimeException("Couldnt parse json:" + json, e);
    }
}

Обратите внимание, что это не удастся, если у вас есть дочерние объекты в вашем json (потому что они не являются a String, они являются другими HashMap), но будет работать, если ваш json является списком ключевых значений свойств следующим образом:

{
    "client_id": "my super id",
    "exp": 1481918304,
    "iat": "1450382274",
    "url": "http://www.example.com"
}
Брэд Паркс
источник
3

Используя Google Gson

Почему бы не использовать Google Gson, как упомянуто здесь ?

Очень прямолинейно и сделал работу за меня:

HashMap<String,String> map = new Gson().fromJson( yourJsonString, new TypeToken<HashMap<String, String>>(){}.getType());
Лоукан ЭльКади
источник
1
Согласен, мир движется дальше, Gson намного, намного проще в использовании.
DJNA
1

Вот общее решение этой проблемы.

public static <K extends Object, V extends Object> Map<K, V> getJsonAsMap(String json, K key, V value) {
    try {
      ObjectMapper mapper = new ObjectMapper();
      TypeReference<Map<K, V>> typeRef = new TypeReference<Map<K, V>>() {
      };
      return mapper.readValue(json, typeRef);
    } catch (Exception e) {
      throw new RuntimeException("Couldnt parse json:" + json, e);
    }
  }

Надеюсь, что когда-нибудь кто-нибудь подумает создать метод util для преобразования в любой тип ключа / значения Map, отсюда и этот ответ :)

NameNotFoundException
источник
-1

просто хотел дать котлин ответ

val propertyMap = objectMapper.readValue<Map<String,String>>(properties, object : TypeReference<Map<String, String>>() {})
Dustin
источник
хм, не уверен, почему это было понижено. Мало того, что эта линия работает для меня, она также дает ключ к правильной работе. Заменив Map <String, String> другим требуемым типом, преобразователь преобразует строку в любую сложную коллекцию, например List <MyObject> или List <Map <String, MyObject >>
Dustin