Como converter uma string JSON em um mapa <String, String> com Jackson JSON

185

Estou tentando fazer algo assim, mas não funciona:

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

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

Mas o IDE diz:

Atribuição não verificada Map to Map<String,String>

Qual é o caminho certo para fazer isso? Estou usando apenas Jackson, porque é isso que já está disponível no projeto. Existe uma maneira nativa de Java de converter de / para JSON?

No PHP, eu simplesmente json_decode($str)e recuperaria uma matriz. Eu preciso basicamente da mesma coisa aqui.

adamJLev
fonte
De onde vem a classe JacksonUtils? Eu não vejo isso em nenhum dos lançamentos de Jackson.
Rob Heiser
É nosso invólucro para Jackson, lida com algumas das coisas de JsonFactory e ObjectMapper que você precisa fazer.
adamJLev
1
Portanto, o problema é que JacksonUtils.fromJSON () não é declarado para retornar Map <String, String>, mas apenas Map.
Rob Heiser
7
Aliás, não atribua um novo HashMap lá na primeira linha: isso é ignorado. Apenas atendendo a ligação.
StaxMan
O título não tem nada a ver com o problema descrito, que tem a ver com a coleção não digitada. A resposta abaixo é a resposta correta para o que você realmente tentou perguntar.
Jukka Dahlbom

Respostas:

313

Eu tenho o seguinte 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); 
}   

Ele está lendo um arquivo, mas mapper.readValue()também aceita um InputStreame você pode obter um InputStreamde uma string usando o seguinte:

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

Há um pouco mais de explicação sobre o mapeador no meu blog .

djna
fonte
2
@Suraj, é conforme a documentação e concordo que não seria capaz de deduzir a formulação dos primeiros princípios. Não é tão estranho quanto mostrar que Java é mais complexo do que poderíamos pensar.
djna
1
Krige: Eu pensei que o pouco difícil foi conseguir o mapeador indo, mas eu adicionei a nota sobre como aplicar a técnica para uma string
djna
2
Um pequeno comentário: a primeira linha de criação JsonFactorynão é necessária. ObjectMapperpode criá-lo automaticamente por conta própria.
23415 StaxMan
1
@djna o cartaz pediu Map<String, String>e você forneceu Map<String, Object>.
anon58192932
Para escrever o Mapa como uma string que você pode fazermapper.writeValueAsString(hashmap)
Zaheer
53

Tente TypeFactory. Aqui está o 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);

Aqui está o código para uma versão mais antiga do Jackson JSON.

Map<String, String> result = new ObjectMapper().readValue(
    data, TypeFactory.mapType(HashMap.class, String.class, String.class));
Ning Sun
fonte
38
TypeFactory.mapType (...) agora está obsoleto, tente o seguinte: new TypeReference <HashMap <String, String >> () {}
cyber-monk
2
@ cyber-monk Isso elimina os avisos, mas na verdade não verifica os tipos.
David Moles
26

O aviso obtido é feito pelo compilador, não pela biblioteca (ou método utilitário).

A maneira mais simples de usar o Jackson diretamente seria:

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

O método utilitário que você chama provavelmente faz algo semelhante a isso.

StaxMan
fonte
O OP pediu Map<String, String>e você forneceu Map<String, Object>.
anon58192932
18
ObjectReader reader = new ObjectMapper().readerFor(Map.class);

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

Observe que a readerinstância é segura para threads.

dpetruha
fonte
@dpetruha OP pediu Map<String, String>e você forneceu Map<String, Object>.
anon58192932
10

Convertendo de String para Mapa JSON:

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

ObjectMapper mapper = new ObjectMapper();

map = mapper.readValue(string, HashMap.class);
Rajá
fonte
4
O acima ainda resulta em Type safety: The expression of type HashMap needs unchecked conversion to conform to Map<String,String>. Enquanto isso pode ser suprimido com @SuppressWarningsanotação, eu recomendo usar TypeReferenceprimeiro ou fundição próxima como mencionado por Staxman
Karthic Raghupathi
1
Para se livrar do aviso de segurança de tipo, você pode usar map = mapper.readValue(string, map.getClass()); - desde que você instanciou o mapa, como é o caso aqui.
MJV
se o tipo de var for Map <Inteiro, String>, é só pegar a classe de objeto que não está correta.
Jiayu Wang
5
JavaType javaType = objectMapper.getTypeFactory().constructParameterizedType(Map.class, Key.class, Value.class);
Map<Key, Value> map=objectMapper.readValue(jsonStr, javaType);

Eu acho que isso vai resolver o seu problema.

JackieZhi
fonte
1
no doc java: @since 2,5 - mas irá provavelmente preterido em 2.7 ou 2.8 (não é necessário com 2.7)
Al-Mothafar
3

O seguinte funciona para mim:

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

Onde getJsonAsMap é definido assim:

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

Note que este irá falhar se você tem objetos filho em sua json (porque eles não são um String, eles são outra HashMap), mas vai funcionar se o JSON é uma lista de valores-chave de imóveis como assim:

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

Usando o Gson do Google

Por que não usar o Gson do Google, como mencionado aqui ?

Muito direto e fez o trabalho para mim:

HashMap<String,String> map = new Gson().fromJson( yourJsonString, new TypeToken<HashMap<String, String>>(){}.getType());
Loukan ElKadi
fonte
1
Concordou, o mundo mudou, Gson é muito, muito mais fácil de usar.
djna
1

Aqui está a solução genérica para esse 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 algum dia alguém pense em criar um método util para converter para qualquer tipo de mapa de chave / valor, portanto, esta resposta :)

NameNotFoundException
fonte
-1

só queria dar uma resposta Kotlin

val propertyMap = objectMapper.readValue<Map<String,String>>(properties, object : TypeReference<Map<String, String>>() {})
Dustin
fonte
hmmm, não sei por que isso foi rebaixado. Essa linha não só funciona para mim, mas também fornece a chave para fazê-lo da maneira certa. Substituindo Map <String, String> por um tipo desejado diferente, o mapeador converterá a string em qualquer coleção complexa, como List <MyObject> ou List <Map <String, MyObject >>
Dustin