Як перетворити рядок JSON в карту <String, String> за допомогою Джексона JSON


184

Я намагаюся зробити щось подібне, але це не працює:

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)і поверну масив. Мені в основному потрібно те саме.


Звідки походить клас JacksonUtils? Я не бачу цього в жодному з випусків Джексона.
Роб Хайзер

Це наша обгортка для Джексона, обробляє деякі речі JsonFactory та ObjectMapper, які вам потрібно зробити.
adamJLev

1
Отже, проблема полягає в тому, що JacksonUtils.fromJSON () не оголошено повернення Map <String, String>, а лише Map.
Роб Хайзер

7
До речі, не призначайте новий HashMap там на першому рядку: це ігнорується. Просто прийняття дзвінка.
StaxMan

Заголовок не має нічого спільного з описаною вами проблемою, що стосується нетипізованого зібрання. Відповідь нижче - це правильна відповідь на те, що ви насправді намагалися запитати.
Jukka Dahlbom

Відповіді:


312

У мене є такий код:

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

У моєму блозі є трохи більше пояснень щодо картографа .


2
@Suraj, це відповідно до документації, і я погоджуюся, що я не зміг би вивести формулювання з перших принципів. Це не так дивно, як показувати, що Java складніша, ніж ми могли б подумати.
djna

1
Krige: Я подумав, що важкий біт заводить картограф, але я додав примітку про те, як застосувати техніку до струни
djna

2
Один незначний коментар: перший рядок створення JsonFactoryне потрібен. ObjectMapperможе створити його автоматично самостійно.
StaxMan

1
@djna плакат попросив, Map<String, String>і ви надали Map<String, Object>.
anon58192932

Щоб написати карту як рядок, ви можете це зробитиmapper.writeValueAsString(hashmap)
Zaheer

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 >> () {}
cyber-monk

2
@ кібермонах Це позбавляється від попереджень, але насправді не перевіряє типи.
Девід Молес

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

Корисний метод, який ви викликаєте, мабуть, просто робить щось подібне до цього.


ОП попросили, 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 OP попросив Map<String, String>і ви надали Map<String, Object>.
anon58192932

10

Перетворення з String на карту JSON:

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

Я думаю, це вирішить вашу проблему.


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 є дочірні об’єкти (оскільки вони не є String, вони - інше HashMap), але вони працюватимуть, якщо ваш json є ключовим списком значень таких властивостей:

{
    "client_id": "my super id",
    "exp": 1481918304,
    "iat": "1450382274",
    "url": "http://www.example.com"
}

3

Використання Google Gson

Чому б не використовувати Gson Google, як згадується тут ?

Дуже прямо вперед і зробив роботу для мене:

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

Сподіваюся, колись хтось подумає створити утилітний метод для перетворення на будь-який тип ключа / значення Карти, отже, ця відповідь :)


-1

просто хотів дати відповідь Котліна

val propertyMap = objectMapper.readValue<Map<String,String>>(properties, object : TypeReference<Map<String, String>>() {})

хммм, не впевнений, чому це було знято. Ця лінія не тільки працює для мене, вона також дає ключ до її правильного шляху. Замінивши Map <String, String> на інший бажаний тип, картограф перетворить рядок у будь-яку складну колекцію, наприклад Список <MyObject> або Список <Карта <String, MyObject >>
Дастін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.