SRobertJames
SRobertJames

Reputation: 9263

Java: Parsing JSON with unknown keys to Map

In Java, I need to consume JSON (example below), with a series of arbitrary keys, and produce Map<String, String>. I'd like to use a standard, long term supported JSON library for the parsing. My research, however, shows that these libraries are setup for deserialization to Java classes, where you know the fields in advance. I need just to build Maps.

It's actually one step more complicated than that, because the arbitrary keys aren't the top level of JSON; they only occur as a sub-object for prefs. The rest is known and can fit in a pre-defined class.

{
    "al" : { "type": "admin", "prefs" : { "arbitrary_key_a":"arbitary_value_a", "arbitrary_key_b":"arbitary_value_b"}},
    "bert" : {"type": "user", "prefs" : { "arbitrary_key_x":"arbitary_value_x", "arbitrary_key_y":"arbitary_value_y"}},
    ...
}

In Java, I want to be able to take that String, and do something like:

people.get("al").get("prefs"); // Returns Map<String, String>

How can I do this? I'd like to use a standard well-supported parser, avoid exceptions, and keep things simple.


UPDATE

@kumensa has pointed out that this is harder than it looks. Being able to do:

people.get("al").getPrefs(); // Returns Map<String, String>
people.get("al").getType();  // Returns String

is just as good.

That should parse the JSON to something like:

public class Person {
    public String type;
    public HashMap<String, String> prefs;
}
// JSON parsed to:
HashMap<String, Person>

Upvotes: 2

Views: 2939

Answers (3)

Abrikot
Abrikot

Reputation: 1005

Having your Person class and using Gson, you can simply do:

final Map<String, Person> result = new Gson().fromJson(json, new TypeToken<Map<String, Person>>() {}.getType());

Then, retrieving prefs is achieved with people.get("al").getPrefs();.

But be careful: your json string is not valid. It shouldn't start with "people:".

Upvotes: 1

azhar
azhar

Reputation: 167

You can use Jackson lib to achieve this. Put the following in pom.xml.

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.8</version>
</dependency>

Refer the following snippet that demonstrates the same.

ObjectMapper mapper = new ObjectMapper();
HashMap<String, Object> people = mapper.readValue(jsonString, new TypeReference<HashMap>(){});

Now, it is deserialized as a Map;

Full example:

import java.io.IOException;
import java.util.HashMap;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class testMain {

    public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {

        String json = "{\"address\":\"3, 43, Cashier Layout, Tavarekere Main Road, 1st Stage, BTM Layout, Ambika Medical, 560029\",\"addressparts\":{\"apartment\":\"Cashier Layout\",\"area\":\"BTM Layout\",\"floor\":\"3\",\"house\":\"43\",\"landmark\":\"Ambika Medical\",\"pincode\":\"560029\",\"street\":\"Tavarekere Main Road\",\"subarea\":\"1st Stage\"}}";

        ObjectMapper mapper = new ObjectMapper();
        HashMap<String, Object> people = mapper.readValue(json, new TypeReference<HashMap>(){});

        System.out.println(((HashMap<String, String>)people.get("addressparts")).get("apartment"));
    }

}

Output: Cashier Layout

Upvotes: 0

Oleg Cherednik
Oleg Cherednik

Reputation: 18255

public static <T> Map<String, T> readMap(String json) {
    if (StringUtils.isEmpty(json))
        return Collections.emptyMap();

    ObjectReader reader = new ObjectMapper().readerFor(Map.class);
    MappingIterator<Map<String, T>> it = reader.readValues(json);

    if (it.hasNextValue()) {
        Map<String, T> res = it.next();
        return res.isEmpty() ? Collections.emptyMap() : res;
    }

    return Collections.emptyMap();
}

All you need to do next, it that check the type of the Object. If it is Map, then you have an object. Otherwise, this is a simple value.

Upvotes: 0

Related Questions