Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GSON: How to move fields to parent object

Tags:

java

json

gson

I'm using Google GSON to transform my Java object to JSON.

Currently I'm having the following structure:

"Step": {
  "start_name": "Start",
  "end_name": "End",
  "data": {
    "duration": {
      "value": 292,
      "text": "4 min."
    },
    "distance": {
       "value": 1009.0,
       "text": "1 km"
    },
    "location": {
       "lat": 59.0000,
       "lng": 9.0000,
       "alt": 0.0
    }
  }
}

Currently a Duration object is inside a Data object. I would like to skip the Data object and move the Duration object to the Step object, like this:

"Step": {
  "start_name": "Start",
  "end_name": "End",
  "duration": {
    "value": 292,
    "text": "4 min."
  },
  "distance": {
     "value": 1009.0,
     "text": "1 km"
  },
  "location": {
     "lat": 59.0000,
     "lng": 9.0000,
     "alt": 0.0
  }
}

How can I do this using GSON?

EDIT: I've tried to use a TypeAdapter to modify the Step.class, but in the write-method I'm not able to add my duration object to the JsonWriter.

like image 442
dhrm Avatar asked Jun 05 '12 14:06

dhrm


2 Answers

You can probably do this by writing, and then registering a custom serializer for Step, and making sure inside it you work with Duration etc, instead of Data.

// registering your custom serializer:
GsonBuilder builder = new GsonBuilder ();
builder.registerTypeAdapter (Step.class, new StepSerializer ());
Gson gson = builder.create ();
// now use 'gson' to do all the work

The code for the custom serializer below, I'm writing off the top of my head. It misses exception handling, and might not compile, and does slow things like create instances of Gson repeatedly. But it represents the kind of thing you'll want to do:

class StepSerializer implements JsonSerializer<Step>
{
  public JsonElement serialize (Step src,
                                Type typeOfSrc,
                                JsonSerializationContext context)
    {
      Gson gson = new Gson ();
      /* Whenever Step is serialized,
      serialize the contained Data correctly.  */
      JsonObject step = new JsonObject ();
      step.add ("start_name", gson.toJsonTree (src.start_name);
      step.add ("end_name",   gson.toJsonTree (src.end_name);

      /* Notice how I'm digging 2 levels deep into 'data.' but adding
      JSON elements 1 level deep into 'step' itself.  */
      step.add ("duration",   gson.toJsonTree (src.data.duration);
      step.add ("distance",   gson.toJsonTree (src.data.distance);
      step.add ("location",   gson.toJsonTree (src.data.location);

      return step;
    }
}
like image 180
ArjunShankar Avatar answered Sep 24 '22 23:09

ArjunShankar


In such case I register TypeAdapter for nested data field. Within the the adapter data's fields are added to parent object. No need to create adapter for enclosing class.

public class Step {
    private String startName;
    private endName;
    @JsonAdapter(JsonFlatMapAdapter.class)
    private Map<String, Object> data;
    ...
}

public class JsonFlatMapAdapter extends TypeAdapter<Map<String, Object>> {

    @Override
    public void write(JsonWriter out, Map<String, Object> value) throws IOException {
        out.nullValue();
        Gson gson = new Gson();
        value.forEach((k,v) -> {
            try {
                out.name(k).jsonValue(gson.toJson(v));
            } catch (IOException e) {
            }
        });
    }

    @Override
    public Map<String, Object> read(JsonReader in) throws IOException {
        return null;
    }

}
like image 31
Oleksandr Tsurika Avatar answered Sep 24 '22 23:09

Oleksandr Tsurika