Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to flatten the JSON hierarchy with Gson?

Tags:

java

json

gson

I use Gson to convert JSON data to Java objects. However, the JSON structure has an extra field which could be flattened. Is this possible to do with Gson?

To elaborate (since this is rather difficult to explain), the JSON looks something like this:

{
    "foo": "bar",
    "data": {
        "first": 0,
        "second": 1,
        "third": 2
    }
}

This produces two classes, one for the parent and one for data, like this:

public class Entry {
    private String foo;
    private Data data;
}

public class Data {
    private int first;
    private int second;
    private int third;
}

I'd like to "flatten" the data field into the parent object so that the Java class would look something like this:

public class Entry {
    private String foo; 
    private int first;
    private int second;
    private int third;
}

Is this possible with Gson, using e.g. TypeAdapters?

like image 330
manabreak Avatar asked Nov 09 '22 15:11

manabreak


1 Answers

I'll show you demo and you decide for yourself do you really want this... Because it makes TypeAdapter code hard to read.

private static class EntryTypeAdapter extends TypeAdapter<Entry> {
    // without registerTypeAdapter(Entry.class, new EntryTypeAdapter())
    private Gson gson = new GsonBuilder()
            // ignore "foo" from deserialization and serialization
            .setExclusionStrategies(new TestExclStrat()).create();

    @Override
    public void write(JsonWriter out, Entry value) throws IOException {
        out.beginObject();
        out.name("foo");
        out.value(value.foo);
        out.name("data");
        out.value(gson.toJson(value));
        out.endObject();
    }

    @Override
    public Entry read(JsonReader in) throws IOException {
        Entry entry = null;
        String foo = null;
        in.beginObject();
        while (in.hasNext()) {
            String name = in.nextName();
            if (name.equals("foo")) {
                foo = in.nextString();
            } else if (name.equals("data")) {
                entry = gson.fromJson(in, Entry.class);
            } else {
                in.skipValue();
            }
        }
        in.endObject();
        if(entry!= null) entry.foo = foo;
        return entry;
    }

    public class TestExclStrat implements ExclusionStrategy {
        public boolean shouldSkipClass(Class<?> arg0) {
            return false;
        }
        public boolean shouldSkipField(FieldAttributes f) {
            return f.getName().equals("foo");
        }
    }
}

Can test it with this:

public static void main(String[] args) throws IOException, JSONException {
    String jsonString = "{\n" +
            "    \"foo\": \"bar\",\n" +
            "    \"data\": {\n" +
            "        \"first\": 0,\n" +
            "        \"second\": 1,\n" +
            "        \"third\": 2\n" +
            "    }\n" +
            "}";

    Gson gson = new GsonBuilder()
                   .registerTypeAdapter(Entry.class, new EntryTypeAdapter()).create();
    Entry el = gson.fromJson(jsonString, Entry.class);
    String serialized = gson.toJson(el);
    System.out.println(serialized);
}

public static class Entry {
    public String foo;
    public Integer first;
    public Integer second;
    public Integer third;
}

You could also do something like this:

// even more complicated version without inner Gson help
public Entry readOption2(JsonReader in) throws IOException {
    Entry entry = new Entry();
    in.beginObject();
    while (in.hasNext()) {
        String name = in.nextName();
        if (name.equals("foo")) {
            entry.foo = in.nextString();
        } else if (name.equals("data")) {
            in.beginObject();
            while (in.hasNext()) {
                name = in.nextName();
                if (name.equals("first")) {
                    entry.first = in.nextInt();
                } else if (name.equals("second")) {
                    entry.second = in.nextInt();
                } else if (name.equals("third")) {
                    entry.third = in.nextInt();
                }else{
                    in.skipValue();
                }
            }
            in.endObject();
        } else {
            in.skipValue();
        }
    }
    in.endObject();
    return entry;
}
like image 165
varren Avatar answered Nov 14 '22 23:11

varren