Comment convertir une chaîne JSON en Map <String, String> avec Jackson JSON

185

J'essaye de faire quelque chose comme ça mais ça ne marche pas:

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

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

Mais l'IDE dit:

Affectation non cochée Map to Map<String,String>

Quelle est la bonne façon de procéder? J'utilise uniquement Jackson parce que c'est ce qui est déjà disponible dans le projet, existe-t-il un moyen Java natif de convertir vers / depuis JSON?

En PHP, je voudrais simplement json_decode($str)et je récupérerais un tableau. J'ai besoin fondamentalement de la même chose ici.

adamJLev
la source
D'où vient la classe JacksonUtils? Je ne le vois dans aucune des versions de Jackson.
Rob Heiser
C'est notre wrapper pour Jackson, qui gère certaines des tâches JsonFactory et ObjectMapper que vous devez faire.
adamJLev
1
Donc, le problème est que JacksonUtils.fromJSON () n'est pas déclaré renvoyer Map <String, String>, mais simplement Map.
Rob Heiser
7
Btw, n'assignez pas de nouveau HashMap sur la première ligne: cela est ignoré. Juste assing the call.
StaxMan
Le titre n'a rien à voir avec le problème que vous avez décrit, qui a à voir avec une collection non typée. La réponse ci-dessous est la bonne réponse à ce que vous avez vraiment essayé de demander.
Jukka Dahlbom

Réponses:

313

J'ai le code suivant:

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

Il lit à partir d'un fichier, mais mapper.readValue()accepte également un InputStreamet vous pouvez obtenir un à InputStreampartir d'une chaîne en utilisant ce qui suit:

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

Il y a un peu plus d'explications sur le mappeur sur mon blog .

djna
la source
2
@Suraj, c'est selon la documentation, et je suis d'accord que je n'aurais pas pu déduire la formulation des premiers principes. Ce n'est pas tellement bizarre que de montrer que Java est plus complexe qu'on ne le pense.
djna
1
Krige: Je pensais que le plus dur était de faire démarrer le mappeur, mais j'ai ajouté la note sur la façon d'appliquer la technique à une chaîne
djna
2
Un petit commentaire: la première ligne de création JsonFactoryn'est pas nécessaire. ObjectMapperpeut le créer automatiquement seul.
StaxMan
1
@djna l'affiche a demandé Map<String, String>et vous avez fourni Map<String, Object>.
anon58192932
Pour écrire la carte sous forme de chaîne, vous pouvez le fairemapper.writeValueAsString(hashmap)
Zaheer
53

Essayez TypeFactory. Voici le code pour 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);

Voici le code d'une ancienne version de Jackson JSON.

Map<String, String> result = new ObjectMapper().readValue(
    data, TypeFactory.mapType(HashMap.class, String.class, String.class));
Ning Sun
la source
38
TypeFactory.mapType (...) est maintenant obsolète, essayez ceci: new TypeReference <HashMap <String, String >> () {}
cyber-monk
2
@ cyber-moine Cela supprime les avertissements, mais ne vérifie pas réellement les types.
David Moles
26

L'avertissement que vous obtenez est fait par le compilateur, pas par la bibliothèque (ou la méthode utilitaire).

Le moyen le plus simple d'utiliser directement Jackson serait:

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

La méthode utilitaire que vous appelez fait probablement quelque chose de similaire.

StaxMan
la source
OP a demandé Map<String, String>et vous avez fourni Map<String, Object>.
anon58192932
18
ObjectReader reader = new ObjectMapper().readerFor(Map.class);

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

Notez que cette readerinstance est Thread Safe.

dpetruha
la source
@dpetruha OP a demandé Map<String, String>et vous avez fourni Map<String, Object>.
anon58192932
10

Conversion de chaîne en carte JSON:

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

ObjectMapper mapper = new ObjectMapper();

map = mapper.readValue(string, HashMap.class);
Raja
la source
4
Ce qui précède entraîne toujours Type safety: The expression of type HashMap needs unchecked conversion to conform to Map<String,String>. Bien que cela puisse être supprimé avec une @SuppressWarningsannotation, je vous recommande d'utiliser le TypeReferencepremier ou le casting suivant, comme mentionné par Staxman
Karthic Raghupathi
1
Pour vous débarrasser de l'avertissement de sécurité de type, vous pouvez utiliser map = mapper.readValue(string, map.getClass());- étant donné que vous avez instancié la carte, comme c'est le cas ici.
MJV
si le type de var est Map <Integer, String>, il suffit de récupérer la classe d'objet.
Jiayu Wang
5
JavaType javaType = objectMapper.getTypeFactory().constructParameterizedType(Map.class, Key.class, Value.class);
Map<Key, Value> map=objectMapper.readValue(jsonStr, javaType);

Je pense que cela résoudra votre problème.

JackieZhi
la source
1
dans java doc: @since 2.5 - mais sera probablement obsolète dans 2.7 ou 2.8 (pas nécessaire avec 2.7)
Al-Mothafar
3

Ce qui suit fonctionne pour moi:

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

getJsonAsMapest défini comme ceci:

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

Notez que cela va échouer si vous avez des objets enfants dans votre JSON (parce qu'ils ne sont pas un String, ils sont une autre HashMap), mais ne fonctionnera que si votre JSON est une liste de valeurs clé des propriétés comme ceci:

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

Utilisation du Gson de Google

Pourquoi ne pas utiliser le Gson de Google comme mentionné ici ?

Très simple et a fait le travail pour moi:

HashMap<String,String> map = new Gson().fromJson( yourJsonString, new TypeToken<HashMap<String, String>>(){}.getType());
Loukan ElKadi
la source
1
D'accord, le monde a évolué, Gson est beaucoup, beaucoup plus facile à utiliser.
djna
1

Voici la solution générique à ce problème.

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

J'espère qu'un jour quelqu'un penserait à créer une méthode util pour convertir en n'importe quel type de carte clé / valeur, d'où cette réponse :)

NameNotFoundException
la source
-1

je voulais juste donner une réponse à Kotlin

val propertyMap = objectMapper.readValue<Map<String,String>>(properties, object : TypeReference<Map<String, String>>() {})
Dustin
la source
hmmm, je ne sais pas pourquoi cela a été refusé. Non seulement cette ligne fonctionne pour moi, mais elle donne également la clé pour le faire de la bonne manière. En remplaçant Map <String, String> par un autre type souhaité, le mappeur convertira la chaîne en une collection complexe, comme List <MyObject>, ou List <Map <String, MyObject >>
Dustin