Jak przekonwertować ciąg JSON na mapę <Ciąg, Ciąg> za pomocą Jackson JSON

184

Próbuję zrobić coś takiego, ale to nie działa:

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

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

Ale IDE mówi:

Niesprawdzone przypisanie Map to Map<String,String>

Jak to zrobić? Używam tylko Jacksona, ponieważ to jest już dostępne w projekcie, czy istnieje natywny sposób konwersji Java do / z JSON?

W PHP po prostu json_decode($str)bym zwrócił tablicę. Potrzebuję tu w zasadzie tej samej rzeczy.

adamJLev
źródło
Skąd pochodzi klasa JacksonUtils? Nie widzę tego w żadnym wydaniu Jacksona.
Rob Heiser
To jest nasze opakowanie dla Jacksona, które obsługuje niektóre rzeczy JsonFactory i ObjectMapper, które musisz zrobić.
adamJLev
1
Problem polega na tym, że JacksonUtils.fromJSON () nie zadeklarował, że zwróci Map <String, String>, a jedynie Map.
Rob Heiser
7
Przy okazji, nie przypisuj nowego HashMap tam w pierwszym wierszu: zostanie to zignorowane. Tylko odbieram połączenie.
StaxMan
Tytuł nie ma nic wspólnego z opisanym problemem, który dotyczy nierozpoznanego zbioru. Poniższa odpowiedź jest prawidłową odpowiedzią na to, o co naprawdę starałeś się zapytać.
Jukka Dahlbom,

Odpowiedzi:

312

Mam następujący kod:

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

Odczytuje z pliku, ale mapper.readValue()akceptuje również ciąg znaków InputStreami można go uzyskać, InputStreamkorzystając z następujących poleceń :

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

Na moim blogu jest trochę więcej wyjaśnień na temat mapera .

djna
źródło
2
@Suraj, to zgodnie z dokumentacją i zgadzam się, że nie byłbym w stanie wydedukować sformułowania z pierwszych zasad. Nie jest to tak dziwne, jak pokazanie, że Java jest bardziej złożona, niż mogłoby się wydawać.
djna
1
Krige: Myślałem, że trudem było uruchomienie mapera, ale dodałem notatkę, jak zastosować tę technikę do struny
djna
2
Jeden drobny komentarz: pierwsza linia tworzenia JsonFactorynie jest potrzebna. ObjectMappermoże utworzyć go samodzielnie.
StaxMan,
1
@djna poprosił o plakat, Map<String, String>a ty dostarczyłeś Map<String, Object>.
anon58192932,
Aby napisać mapę jako ciąg, możesz zrobićmapper.writeValueAsString(hashmap)
Zaheer
53

Spróbować TypeFactory. Oto kod dla Jackson 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);

Oto kod starszej wersji Jackson JSON.

Map<String, String> result = new ObjectMapper().readValue(
    data, TypeFactory.mapType(HashMap.class, String.class, String.class));
Ning Sun
źródło
38
TypeFactory.mapType (...) jest już przestarzały, spróbuj tego: nowy TypeReference <HashMap <Łańcuch, Łańcuch >> () {}
cyber-monk
2
@ cyber-monk Pozbywa się ostrzeżeń, ale nie sprawdza typów.
David Moles,
26

Ostrzeżenie pojawia się przy użyciu kompilatora, a nie biblioteki (lub metody narzędziowej).

Najprostszym sposobem bezpośredniego użycia Jacksona byłoby:

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

Wywołana metoda narzędziowa prawdopodobnie robi coś podobnego do tego.

StaxMan
źródło
PO poprosił Map<String, String>i podasz Map<String, Object>.
anon58192932,
18
ObjectReader reader = new ObjectMapper().readerFor(Map.class);

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

Pamiętaj, że readerinstancja jest Bezpieczna dla wątków.

dpetruha
źródło
@dpetruha OP poprosił Map<String, String>i podasz Map<String, Object>.
anon58192932,
10

Konwertowanie z ciągu na mapę JSON:

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

ObjectMapper mapper = new ObjectMapper();

map = mapper.readValue(string, HashMap.class);
Radża
źródło
4
Powyższe nadal skutkuje Type safety: The expression of type HashMap needs unchecked conversion to conform to Map<String,String>. Chociaż można to stłumić za pomocą @SuppressWarningsadnotacji, polecam użycie TypeReferencepierwszego lub castingowania następnego, o czym wspomniał Staxman
Karthic Raghupathi
1
Aby pozbyć się ostrzeżenia o bezpieczeństwie typu, możesz użyć map = mapper.readValue(string, map.getClass());- pod warunkiem, że utworzyłeś instancję mapy, jak ma to miejsce w tym przypadku.
MJV,
jeśli typem zmiennej jest Map <Integer, String>, po prostu pobierz klasę obiektu jest nieprawidłowa.
Jiayu Wang,
5
JavaType javaType = objectMapper.getTypeFactory().constructParameterizedType(Map.class, Key.class, Value.class);
Map<Key, Value> map=objectMapper.readValue(jsonStr, javaType);

myślę, że to rozwiąże twój problem.

JackieZhi
źródło
1
in java doc: @since 2.5 - ale prawdopodobnie przestanie obowiązywać w wersji 2.7 lub 2.8 (niepotrzebne w wersji 2.7)
Al-Mothafar
3

Poniższe działa dla mnie:

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

gdzie getJsonAsMapjest zdefiniowane tak:

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

Zauważ, że to się nie powiedzie, jeśli masz obiekty potomne w swoim Jsonie (ponieważ nie są one String, to inne HashMap), ale zadziała, jeśli twój Json jest listą kluczowych wartości właściwości takich jak:

{
    "client_id": "my super id",
    "exp": 1481918304,
    "iat": "1450382274",
    "url": "http://www.example.com"
}
Brad Parks
źródło
3

Korzystanie z Google Gson

Dlaczego nie skorzystać z Google Gson, jak wspomniano tutaj ?

Bardzo prosto i wykonałem dla mnie pracę:

HashMap<String,String> map = new Gson().fromJson( yourJsonString, new TypeToken<HashMap<String, String>>(){}.getType());
Loukan ElKadi
źródło
1
Uzgodniony, świat się zmienił, Gson jest znacznie łatwiejszy w użyciu.
djna
1

Oto ogólne rozwiązanie tego problemu.

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

Mam nadzieję, że pewnego dnia ktoś pomyśli o stworzeniu metody util do konwersji na dowolny klucz / wartość typu mapy, stąd ta odpowiedź :)

NameNotFoundException
źródło
-1

chciałem tylko dać odpowiedź Kotlin

val propertyMap = objectMapper.readValue<Map<String,String>>(properties, object : TypeReference<Map<String, String>>() {})
Dustin
źródło
hmmm, nie jestem pewien, dlaczego to zostało odrzucone. Ta linia nie tylko działa dla mnie, ale także daje klucz do zrobienia tego we właściwy sposób. Zastępując Map <String, String> innym pożądanym typem, program mapujący przekształci ciąg w dowolną złożoną kolekcję, taką jak List <MyObject> lub List <Map <String, MyObject >>
Dustin