How can I deserialize a JSON object but keep a specific field as a String instead of a nested object?

1.5k views Asked by At

I have a json structure which I've pasted below. I'd like to deserialize the json into a java POJO using Gson which is pretty straight-forward, except that I want to keep one of the fields, data, as a String type instead of a nested object.

JSON structure

{
  "created_on": "2015-06-04T16:12:04-0700",
  "modified_on": "2015-06-04T16:12:09-0700",
  "identifier": "sample",
  "name": "some name",
  "summary": "some summary",
  "data": {
    "$type": "a_type",
    "some_other_stuff": {
        "more_stuff": "lorem ipsum"
    },
    "type2": {
        "$type": "another_type",
        "other_stuff": {
            "event_more_stuff": "lorem ipsum"
        }
    }
  }
}

My POJO would then look like this:

public class Sample {
  private String identifier; // "sample"
  private String created_on; // "2015-06-04T16:12:04-0700"
  private String modified_on; // "2015-06-04T16:12:09-0700"
  private String name; // "some name"
  private String summary; // "some summary"
  private String data; // "{ \"$type\": ... }"

  // getters and setters
}

The data field should remain as a JSON-formatted String.

I've tried implementing a custom TypeAdapter and reading the field as a String, but it fails with Expected a string but was BEGIN_OBJECT.

Also please note, I would like the structure to be maintained on serialization as well - so I can serialize the POJO back to the original JSON structure.

Edit Custom TypeAdapter:

public class SampleTypeAdapter extends TypeAdapter<Sample> {
@Override
public void write(JsonWriter out, Sample sample) throws IOException {
    out.beginObject();

    out.name("identifier").value(sample.getIdentifier());
    out.name("name").value(sample.getName());
    out.name("data").value(sample.getData());
    out.name("summary").value(sample.getSummary());
    out.name("modified_on").value(sample.getModifiedOn());
    out.name("created_on").value(sample.getCreatedOn());

    out.endObject();
}

@Override
public Sample read(JsonReader in) throws IOException {
    final Sample sample = new Sample();

    in.beginObject();
    while (in.hasNext()) {
        String nextName = in.nextName();
        switch (nextName) {
            case "identifier":
                sample.setIdentifier(in.nextString());
                break;
            case "name":
                sample.setName(in.nextString());
                break;
            case "data":
                sample.setData(in.nextString()); // <-- fails here
                break;
            case "summary":
                sample.setSummary(in.nextString());
                break;
            case "modified_on":
                sample.setModifiedOn(in.nextString());
                break;
            case "created_on":
                sample.setCreatedOn(in.nextString());
                break;
            default:
                in.skipValue();
                break;
        }
    }
    in.endObject();

    return sample;
}
}
2

There are 2 answers

0
Cata On BEST ANSWER

You could create a custom JsonDeserializer like this one:

public class SampleGSONParserAdapter implements
        JsonDeserializer<Sample> {

    @Override
    public Sample deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

        Sample sample = new Sample();
        JsonObject sampleJsonObject = json.getAsJsonObject();

        sample.setName(sampleJsonObject.get("name").getAsString());

        // do the other parsing stuff here..

        // getting the data object as a string
        sample.setJsonString(sampleJsonObject.get("data").toString());

        return sample;
    }

}

And you use it like this:

GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.registerTypeAdapter(Sample.class, new SampleGSONParserAdapter());
Gson gson = gsonBuilder.create();

The bad part is that is not as fast as the one you wrote but at least you can do custom parsing like this one.

3
TommySM On

Use this custom class, add what you need:

 public class JsonParser 
{

    public static <T> List<T> parseList(String json_text, String json_path, Class<T> c)
    {
        Gson gson = new Gson();
        try
        {
            List<T> json_list = new ArrayList<>();
            List<Object> nodes = JsonPath.read(json_text, json_path);

            for(Object node : nodes)
            {
                json_list.add(gson.fromJson(node.toString(), c));
            }
            return (json_list);
        }
        catch(Exception e)
        {
            return (new ArrayList<>());
        }
    }
    public static <T> T parseObject(String json_text, Class<T> c)
    {
        return (new Gson()).fromJson(json_text, c);
    }

    public static <T> T getItemAtPosition (String json_text, String json_path)
    {
        try
        {
            return (JsonPath.read(json_text, json_path));
        }
        catch(Exception e)
        {
            return (null);
        }
    }
}

FINAL EDIT:

Since what you want is only part of your JSON to remain a JSONed String, of all this you might just need (which means just JsonPath):

String myData = JsonParser.getItemAtPosition(response,"$.data[*]").toString();