1

For example:

{
    "id" : "123",
    "name" : "Tom",
    "class" : {
        "subject" : "Math",
        "teacher" : "Jack"
    }
}

I want to get the Map<String, String>:

"id" : "123",
"name" : "Tom",
"subject" : "Math",
"teacher" : "Jack"
evanmcdonnal
  • 38,588
  • 13
  • 84
  • 107
Derek Zhang
  • 131
  • 4
  • 12
  • What is your question? How to do this? You mention two libraries that can do this conversion. Stack Overflow isn't generally the right place to ask "Which comparable library is better" type questions. – emerssso Apr 20 '15 at 23:24
  • also, see: http://stackoverflow.com/questions/2525042/how-to-convert-a-json-string-to-a-mapstring-string-with-jackson-json – emerssso Apr 20 '15 at 23:25
  • I am not asking which library is better. I actually asked how to use Jackson to flatten a json to an object of Map. If Jackson is not possible, how about Gson? – Derek Zhang Apr 21 '15 at 03:41
  • 2
    @greg-449 The question is not duplicate; it asks about returning a flattened map. The linked question returns a recursive map not a flattened one. – Random42 Apr 21 '15 at 08:04
  • I agree with @m3th0dman. This duplicate is not useful to answer this question. Voted to reopen. – Alexis C. Apr 21 '15 at 09:05
  • It would be good to know why you are imposing the limitation of using Gson or Jackson? Neither of these libraries, currently, provide the functionality to flatten JSON. However, if your aim is just to flatten JSON then there are a number of libraries which provide robust methods which specifically address this problem (e.g. https://github.com/wnameless/json-flattener). – neilireson Jun 30 '17 at 08:54

3 Answers3

3

I'm not sure if something exists out of the box (speaking about Gson). However you could write a custom recursive deserializer:

Type t = new TypeToken<Map<String, String>>(){}.getType();
Gson gson = new GsonBuilder().registerTypeAdapter(t, new FlattenDeserializer()).create();
Map<String, String> map = gson.fromJson(new FileReader(new File("file")), t);

...

class FlattenDeserializer implements JsonDeserializer<Map<String, String>> {
    @Override
    public Map<String, String> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        Map<String, String> map = new LinkedHashMap<>();

        if (json.isJsonArray()) {
            for (JsonElement e : json.getAsJsonArray()) {
                map.putAll(deserialize(e, typeOfT, context));
            }
        } else if (json.isJsonObject()) {
            for (Map.Entry<String, JsonElement> entry : json.getAsJsonObject().entrySet()) {
                if (entry.getValue().isJsonPrimitive()) {
                    map.put(entry.getKey(), entry.getValue().getAsString());
                } else {
                    map.putAll(deserialize(entry.getValue(), typeOfT, context));
                }
            }
        }
        return map;
    }
}

Given your example it outputs:

{id="123", name="Tom", subject="Math", teacher="Jack"}

although it doesn't handle the case when a key is mapped to an array of primitives (because it's not possible to map a key with multiple values in a Map<String, String> (unless you can take the String representation of the array or that you can return a Map<String, Object>) but it works for an array of objects (given that each object has a unique key).

So if you have:

{
    "id": "123",
    "name": "Tom",
    "class": {
        "keys": [
            {
                "key1": "value1"
            },
            {
                "key2": "value2"
            }
        ],
        "teacher": "Jack"
    }
}

it'll output:

{id="123", name="Tom", key1="value1", key2="value2", teacher="Jack"}

You can customize it as you need if more cases are needed to be handled.

Hope it helps! :)

Alexis C.
  • 82,826
  • 18
  • 154
  • 166
0

Using Jackson:

private static <T> T fromJsonToGenericPojo(ObjectMapper objectMapper, 
                   String json, Class<?> classType, Class<?>... genericTypes)
{
    JavaType javaType = TypeFactory.defaultInstance()
                    .constructParametricType(classType, genericTypes);
    try {
        return objectMapper.readValue(json, javaType);
    } catch (IOException e) {
        LOGGER.error(e.getMessage(), e);
        throw new IllegalStateException(e);
    }
}

you can get a map of maps:

Map<String, Object> map = fromJsonToGenericPojo(objectMapper, json,    
                                 Map.class, String.class, Object.class);

Once you have the map you can flatten it using the JDK as you wish. There isn't a generic way to flatten a map; what if you have the JSON:

{
    "id" : "123",
    "name" : "Tom",
    "class" : [
    {
        "subject" : "Math",
        "teacher" : "Jack"
    }, 
    {
        "subject" : "English",
        "teacher" : "John"
    }]
}

Regarding elements in an array you will have conflicting keys. What will your resolution be? Only the last value, only the first value, will you change the key name from subject to subject1 - "Math", subject2 - "English", will you not add any of them?

Random42
  • 8,072
  • 6
  • 48
  • 79
0

Use ObjectMapper to convert Json to an Java Class.Later have an Map in it as a variable.

ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(new File("c:\\user.json"), User.class);

For more reference.

Narendra Pandey
  • 369
  • 1
  • 8
  • 25