Cómo convertir una cadena JSON en un Mapa <Cadena, Cadena> con Jackson JSON

185

Estoy tratando de hacer algo como esto pero no funciona:

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

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

Pero el IDE dice:

Asignación no verificada Map to Map<String,String>

¿Cuál es la forma correcta de hacer esto? Solo estoy usando Jackson porque eso es lo que ya está disponible en el proyecto, ¿hay una forma nativa de Java de convertir a / desde JSON?

En PHP simplemente json_decode($str)lo haría y obtendría una matriz. Necesito básicamente lo mismo aquí.

adamJLev
fuente
¿De dónde viene la clase JacksonUtils? No lo veo en ninguno de los lanzamientos de Jackson.
Rob Heiser
Es nuestro contenedor para Jackson, maneja algunas de las cosas de JsonFactory y ObjectMapper que tienes que hacer.
adamJLev
1
Entonces, el problema es que JacksonUtils.fromJSON () no se declara para devolver Map <String, String>, sino solo Map.
Rob Heiser
77
Por cierto, no asigne un nuevo HashMap allí en la primera línea: eso se ignora. Solo estoy evaluando la llamada.
StaxMan
El título no tiene nada que ver con su problema descrito, que tiene que ver con una colección sin tipo. La respuesta a continuación es la respuesta correcta a lo que realmente trató de preguntar.
Jukka Dahlbom

Respuestas:

313

Tengo el siguiente código:

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

Está leyendo desde un archivo, pero mapper.readValue()también aceptará un InputStreamy puede obtener uno InputStreamde una cadena usando lo siguiente:

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

Hay un poco más de explicación sobre el mapeador en mi blog .

djna
fuente
2
@Suraj, según la documentación, y estoy de acuerdo en que no habría podido deducir la formulación de los primeros principios. No es tan extraño como demostrar que Java es más complejo de lo que podríamos pensar.
djna
1
Krige: Pensé que lo difícil era poner en marcha el mapeador, pero agregué la nota sobre cómo aplicar la técnica a una cadena
djna
2
Un comentario menor: la primera línea de creación JsonFactoryno es necesaria. ObjectMapperpuede crearlo automáticamente por sí solo.
StaxMan
1
@djna solicitó el póster Map<String, String>y usted lo ha proporcionado Map<String, Object>.
anon58192932
Para escribir el mapa como una cadena que puede hacermapper.writeValueAsString(hashmap)
Zaheer
53

Tratar TypeFactory. Aquí está el código para 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);

Aquí está el código para una versión anterior de Jackson JSON.

Map<String, String> result = new ObjectMapper().readValue(
    data, TypeFactory.mapType(HashMap.class, String.class, String.class));
Ning Sun
fuente
38
TypeFactory.mapType (...) ahora está en desuso, intente esto: new TypeReference <HashMap <String, String >> () {}
cyber-monk
2
@ cyber-monk Eso elimina las advertencias, pero en realidad no verifica los tipos.
David Moles
26

La advertencia que obtienes se realiza por compilador, no por biblioteca (o método de utilidad).

La forma más sencilla de usar Jackson directamente sería:

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

El método de utilidad que llamas probablemente solo hace algo similar a esto.

StaxMan
fuente
OP solicitó Map<String, String>y usted ha proporcionado Map<String, Object>.
anon58192932
18
ObjectReader reader = new ObjectMapper().readerFor(Map.class);

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

Tenga en cuenta que la readerinstancia es segura para subprocesos.

dpetruha
fuente
@dpetruha OP solicitó Map<String, String>y usted proporcionó Map<String, Object>.
anon58192932
10

Conversión de Cadena a Mapa JSON:

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

ObjectMapper mapper = new ObjectMapper();

map = mapper.readValue(string, HashMap.class);
Raja
fuente
44
Lo anterior todavía resulta en Type safety: The expression of type HashMap needs unchecked conversion to conform to Map<String,String>. Si bien esto se puede suprimir con @SuppressWarningsanotaciones, recomendaría usar TypeReferenceprimero o lanzar después como lo menciona Staxman
Karthic Raghupathi
1
Para deshacerse del tipo de advertencia de seguridad, puede usar map = mapper.readValue(string, map.getClass());, dado que ha instanciado el mapa, como es el caso aquí.
MJV
si el tipo de var es Map <Integer, String>, solo obtenga que la clase de objeto no es correcta.
Jiayu Wang
5
JavaType javaType = objectMapper.getTypeFactory().constructParameterizedType(Map.class, Key.class, Value.class);
Map<Key, Value> map=objectMapper.readValue(jsonStr, javaType);

Creo que esto resolverá tu problema.

JackieZhi
fuente
1
en java doc: @since 2.5 - pero probablemente quedará obsoleto en 2.7 o 2.8 (no se necesita con 2.7)
Al-Mothafar
3

Lo siguiente funciona para mí:

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

donde getJsonAsMapse define así:

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

Tenga en cuenta que esto va a fallar si tiene objetos secundarios en su JSON (porque no son una String, son otro HashMap), pero funcionará si su JSON es una lista de valores clave de las propiedades, así:

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

Usando el Gson de Google

¿Por qué no usar Google's Gson como se menciona aquí ?

Muy sencillo e hizo el trabajo por mí:

HashMap<String,String> map = new Gson().fromJson( yourJsonString, new TypeToken<HashMap<String, String>>(){}.getType());
Loukan ElKadi
fuente
1
De acuerdo, el mundo ha avanzado, Gson es mucho, mucho más fácil de usar.
djna
1

Aquí está la solución genérica a este problema.

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

Espero que algún día alguien piense crear un método util para convertir a cualquier tipo de clave / valor del mapa, de ahí esta respuesta :)

NameNotFoundException
fuente
-1

solo quería dar una respuesta a Kotlin

val propertyMap = objectMapper.readValue<Map<String,String>>(properties, object : TypeReference<Map<String, String>>() {})
Dustin
fuente
hmmm, no estoy seguro de por qué esto fue rechazado. Esta línea no solo funciona para mí, sino que también me da la clave para hacerlo de la manera correcta. Al reemplazar Map <String, String> con un tipo deseado diferente, el asignador convertirá la cadena en cualquier colección compleja, como List <MyObject> o List <Map <String, MyObject >>
Dustin