How to get additional fields in a json data in GSON

I have a JSON object which is actually a java object serialized. The JSON object is something like this

{
  user: 'user',
  telephone: '123456789'
}

The java object's telephone field was renamed from 'telephone' to 'mobile'. So now the java object is has following fields.

  • user
  • mobile

What i want to do is a data migration. I want to get the data from the database as a JSON string -> and parse that JSON to java object using GSON (So far we are used gson.fromJson(class, data) method) preserving the value in the telephone (since telephone is not a field in the java class now) - so then i can pass that data to the new field (mobile).

How can i do this?

P.S. :- This cannot achieve by some simple database query because we are using a file storage system which don't have query facility and encoded values.

P.S. :- Also, we are using the entity event framework


You need to write custom deserialiser and map telephone to mobile:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.JsonAdapter;

import java.io.File;
import java.io.FileReader;
import java.lang.reflect.Type;

public class GsonApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        Gson gson = new GsonBuilder()
                .create();

        try (FileReader jsonReader = new FileReader(jsonFile)) {
            System.out.println(gson.fromJson(jsonReader, User.class));
        }
    }
}

class UserJsonDeserializer implements JsonDeserializer<User> {

    @Override
    public User deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        JsonObject jsonObject = json.getAsJsonObject();

        User user = new User();
        user.setUser(jsonObject.get("user").getAsString());
        user.setMobile(jsonObject.get("telephone").getAsString());

        return user;
    }
}

@JsonAdapter(UserJsonDeserializer.class)
class User {

    private String user;
    private String mobile;

    // getters, setters, toString
}

Prints:

User{user='user', mobile='123456789'}

In case another google wanderer finds this question, there is a way to process extra tags without completely recreating the de-serialization. Based on an example given here. The basic premise is this factory gets the first go at deserializing the class, it uses the next possible type adapter for the class to handle the initial deserialize, does its post processing and returns the result.

class UserTypeAdapterFactory implements TypeAdapterFactory {
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        if (!User.class.isAssignableFrom(type.getRawType())) return null; // this class only serializes 'User' and its subtypes
        final TypeAdapter<User> userAdapter = gson.getDelegateAdapter(this, TypeToken.get(User.class)); // next adapter that isn't this one
        final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
        TypeAdapter<User> result = new TypeAdapter<User>() {
            public void write(JsonWriter out, User value) throws IOException {
                userAdapter.write(out, value);
            }
            public User read(JsonReader in) throws IOException {
                JsonObject object = elementAdapter.read(in).getAsJsonObject();
                User profile = userAdapter.fromJsonTree(object);
                // apply old field to the new, if found (or whatever other post processing)
                try { profile.mobile = object.get("telephone").getAsString(); }
                catch (Exception ignored) {}
                return profile;
            }
        }.nullSafe();
        return (TypeAdapter<T>) result;
    }
}

Then you add to your Gson object something like this and use as normal

private Gson createGson() {
    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapterFactory(new UserTypeAdapterFactory());
    return gsonBuilder.create();
}

Note in this case JsonReader/JsonWriter are from gson library, there may be other implementations, so mind the imports.

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;