2

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>
4
  • Take a look at Gson. It could do that. Commented May 10, 2019 at 7:15
  • It looks like your people json property should be an array. Commented May 10, 2019 at 7:25
  • 2
    But what about people.get("al").get("type");? It cannot return a Map. You'd need an arbitrary tree node and checking content types by yourself. Now if it was more about people.get("al").getPrefs() that returns a Map, it's doable. Commented May 10, 2019 at 7:27
  • @kumensa people.get("al").getPrefs() would be fully acceptable Commented May 10, 2019 at 7:32

3 Answers 3

1

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:".

Sign up to request clarification or add additional context in comments.

Comments

0
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.

Comments

0

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

2 Comments

Ah, yes. The famous get() method of String. (No you can't do that)
Sorry, my bad I missed that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.