Pankaj
Pankaj

Reputation: 5250

Gson nested object issue

Using Gson 2.2.4 with nested objects and JsonReader for streaming.

JSON:

{
"name": "David",
"address": {
    "city": "Bangalore"
},
"role": "Manager"
}

POJO Classes:

public class Data {

    private String name;
    private String role;
    private Address address;

    public String getRole() {
        return role;
    }
    public void setRole(String role) {
        this.role = role;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public String toString(){
        return getName() + "::"+getRole()+"::"+getAddress().getCity();
    }
}


public class Address {

    private String city;

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

}

My Code:

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;

public class TestReader {

    public static void main(String[] args) throws IOException {
        InputStream is = new FileInputStream("test.txt");
        InputStreamReader isr = new InputStreamReader(is);

        JsonReader reader = new JsonReader(isr);

        Data data = new Data();
        data.setAddress(new Address());
        String key = null;

        while (reader.hasNext()) {
            JsonToken token = reader.peek();

            switch (token) {
            case BEGIN_OBJECT:
                reader.beginObject();
                break;
            case END_OBJECT:
                reader.endObject();
                break;
            case BEGIN_ARRAY:
                reader.beginArray();
                break;
            case END_ARRAY:
                reader.endArray();
                break;
            case NAME:
                key = reader.nextName();
                break;
            case STRING:
                if ("name".equals(key))
                    data.setName(reader.nextString());
                else if ("role".equals(key))
                    data.setRole(reader.nextString());
                else if ("city".equals(key))
                    data.getAddress().setCity(reader.nextString());
                break;
            case NULL:
                reader.nextNull();
                break;
            case END_DOCUMENT:
                break;
            default:
                break;

            }
        }
        reader.close();

        System.out.println(data);
    }
}

It prints "David::null::Bangalore" and not able to parse anything after nested object. If I change JSON to

{
"name": "David",
"role": "Manager",
"address": {
    "city": "Bangalore"
}
}

It works fine and prints "David::Manager::Bangalore". Tried a lot but not sure what I am missing.

Upvotes: 1

Views: 3508

Answers (1)

giampaolo
giampaolo

Reputation: 6934

Since JSON is recursive by nature, also your method should. That is you need to call again your method on Address object instead of Data.

So I fixed your code (I admin it, into a naive way) to work in this specific case. If you have a more complex structure, of course method get more complex too.

package stackoverflow.questions.q19282973;

import java.io.*;

import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;

public class TestReader {

    public static void main(String[] args) throws IOException {
        String json = "{\"name\": \"David\",\"address\": {\"city\": \"Bangalore\"},\"role\": \"Manager\"}";
        String json2 = "{\"name\": \"David\",\"role\": \"Manager\",\"address\": {\"city\": \"Bangalore\"}}";

        InputStream is = new ByteArrayInputStream(json2.getBytes("UTF-8"));
        InputStreamReader isr = new InputStreamReader(is);

        JsonReader reader = new JsonReader(isr);

        Data data = new Data();
        data.setAddress(new Address());
        String key = null;

        key = read(reader, data, key);
        reader.close();

        System.out.println(data);
    }

    private static String read(JsonReader reader, Object obj, String key)
            throws IOException {


        while (reader.hasNext()) {
            JsonToken token = reader.peek();

            switch (token) {
            case BEGIN_OBJECT:
                reader.beginObject();
                if ("address".equals(key)) {

                    while (reader.hasNext()) {
                        read(reader, ((Data) obj).getAddress(), key);
                    }
                    reader.endObject();
                }
                break;
            case END_OBJECT:
                reader.endObject();
                break;
            case BEGIN_ARRAY:
                reader.beginArray();
                break;
            case END_ARRAY:
                reader.endArray();
                break;
            case NAME:
                key = reader.nextName();
                break;
            case STRING:
                if (obj instanceof Data) {
                    Data data = (Data) obj;
                    if ("name".equals(key))
                        data.setName(reader.nextString());
                    else if ("role".equals(key))
                        data.setRole(reader.nextString());
                }
                if (obj instanceof Address) {
                    Address address = (Address) obj;
                    if ("city".equals(key))
                        address.setCity(reader.nextString());
                }
                break;
            case NULL:
                reader.nextNull();
                break;
            case END_DOCUMENT:
                break;
            default:
                break;

            }
        }
        return key;
    }
}

But why you want to user a JsonReader? It should be used only for particular cases and anycase it's a very low level Gson class.

You need only something like this

Data d = new Gson().fromJson(yourJsonString, Data.class) to parse.

Or if you want to parse only specific parts of your JSON, you could write your own deserializer.

Upvotes: 3

Related Questions