202

I'm trying to do something like this but it doesn't work:

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

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

But the IDE says:

Unchecked assignment Map to Map<String,String>

What's the right way to do this? I'm only using Jackson because that's what is already available in the project, is there a native Java way of converting to/from JSON?

In PHP I would simply json_decode($str) and I'd get back an array. I need basically the same thing here.

Elrond_EGLDer
  • 47,430
  • 25
  • 189
  • 180
adamJLev
  • 12,986
  • 11
  • 57
  • 62
  • Where is the class JacksonUtils coming from? I don't see it in any of the Jackson releases. – Rob Heiser Mar 26 '10 at 17:54
  • It's our wrapper for Jackson, handles some of the JsonFactory and ObjectMapper stuff that you have to do. – adamJLev Mar 26 '10 at 17:59
  • 1
    So, the problem is that JacksonUtils.fromJSON() isn't declared to return Map, but just Map. – Rob Heiser Mar 26 '10 at 18:04
  • 7
    Btw, don't assign new HashMap there on first line: that gets ignored. Just assing the call. – StaxMan Mar 26 '10 at 18:20
  • The title has nothing to do with your described problem, which has to do with untyped collection. The answer below is the correct answer to what you really tried to ask. – Jukka Dahlbom Dec 17 '12 at 12:26

10 Answers10

337

[Update Sept 2020] Although my original answer here, from many years ago, seems to be helpful and is still getting upvotes, I now use the GSON library from Google, which I find to be more intuitive.

I've got the following code:

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

It's reading from a file, but mapper.readValue() will also accept an InputStream and you can obtain an InputStream from a string by using the following:

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

There's a bit more explanation about the mapper on my blog.

djna
  • 52,574
  • 11
  • 70
  • 109
  • 2
    @Suraj, it's as per the documentation, and I agree I would not have been able to deduce the formulation from first principles. It's not so much weird as showing that Java is more complex than we might think. – djna Oct 30 '12 at 22:02
  • 1
    Krige: I thought the hard bit was getting the mapper going, but I've added the note on how to apply the technique to a string – djna Apr 15 '13 at 21:53
  • 2
    One minor comment: the first line of creating `JsonFactory` is not needed. `ObjectMapper` can create it automatically on its own. – StaxMan Oct 06 '15 at 18:31
  • 1
    @djna the poster asked for `Map` and you've provided `Map`. – anon58192932 Jul 10 '17 at 22:44
  • To write the Map as a string you can do `mapper.writeValueAsString(hashmap)` – Zaheer Dec 07 '17 at 21:31
58

Try TypeFactory. Here's the code for 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);

Here's the code for an older version of Jackson JSON.

Map<String, String> result = new ObjectMapper().readValue(
    data, TypeFactory.mapType(HashMap.class, String.class, String.class));
Nathan
  • 6,095
  • 6
  • 42
  • 63
Ning Sun
  • 1,987
  • 1
  • 18
  • 24
28

Warning you get is done by compiler, not by library (or utility method).

Simplest way using Jackson directly would be:

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

Utility method you call probably just does something similar to this.

djna
  • 52,574
  • 11
  • 70
  • 109
StaxMan
  • 102,903
  • 28
  • 190
  • 229
19
ObjectReader reader = new ObjectMapper().readerFor(Map.class);

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

Note that reader instance is Thread Safe.

StaxMan
  • 102,903
  • 28
  • 190
  • 229
dpetruha
  • 1,064
  • 10
  • 11
10

Converting from String to JSON Map:

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

ObjectMapper mapper = new ObjectMapper();

map = mapper.readValue(string, HashMap.class);
arrowd
  • 30,130
  • 7
  • 72
  • 96
Raja
  • 133
  • 1
  • 2
  • 4
    The above still results in `Type safety: The expression of type HashMap needs unchecked conversion to conform to Map`. While this can be suppressed with `@SuppressWarnings` annotation, I'd recommend using `TypeReference` first or casting next as mentioned by Staxman – k427h1c Jul 25 '12 at 14:30
  • 1
    To get rid of the type safety warning, you can use `map = mapper.readValue(string, map.getClass());` - given that you have instantiated the map, as is the case here. – MJV Mar 16 '16 at 08:53
  • if the type of var is Map, Just get the object class is not right. – Jiayu Wang Jun 15 '17 at 05:20
5
JavaType javaType = objectMapper.getTypeFactory().constructParameterizedType(Map.class, Key.class, Value.class);
Map<Key, Value> map=objectMapper.readValue(jsonStr, javaType);

i think this will solve your problem.

JackieZhi
  • 51
  • 1
  • 2
3

The following works for me:

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

where getJsonAsMap is defined like so:

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 that this will fail if you have child objects in your json (because they're not a String, they're another HashMap), but will work if your json is a key value list of properties like so:

{
    "client_id": "my super id",
    "exp": 1481918304,
    "iat": "1450382274",
    "url": "http://www.example.com"
}
Brad Parks
  • 54,283
  • 54
  • 221
  • 287
2

Using Google's Gson

Why not use Google's Gson as mentioned in here?

Very straight forward and did the job for me:

HashMap<String,String> map = new Gson().fromJson( yourJsonString, new TypeToken<HashMap<String, String>>(){}.getType());
Loukan ElKadi
  • 2,017
  • 14
  • 16
2

just wanted to give a Kotlin answer

val propertyMap = objectMapper.readValue<Map<String,String>>(properties, object : TypeReference<Map<String, String>>() {})
Dustin
  • 242
  • 2
  • 10
  • hmmm, not sure why this was downvoted. Not only does this line work for me, it also gives the key to doing it the right way. By replacing Map with a different desired type, the mapper will convert the string into any complex collection, like List, or List> – Dustin May 08 '20 at 13:59
  • Thx, I was looking for the Kotlin answer. – Benjamin Oct 01 '20 at 12:55
1

Here is the generic solution to this problem.

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

Hope someday somebody would think to create a util method to convert to any Key/value type of Map hence this answer :)