Search code examples
javajsongsonjson-deserialization

Using Gson to load Json into java class with parameterised constructor


I have a main class MainDataObject in which I am loading the json.

    public class MainDataObject {
    private Class<?> jobClass;
    private int time;
    private String tables;

    public MainDataObject(String jobClassname){
        jobClass =  (Class<?>)Class.forName(jobClassname);

     }

In json, jobClass is a string, but I need to load it as a Class object. I have tried writing custom Deserializer, but it didn't work.

    public class ClassDeserializer implements JsonDeserializer<MainDataObject> {

    @Override
    public MainDataObject deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
                    throws JsonParseException {
            String jobClassName = json.getAsJsonObject().get("jobClass").getAsString();
            MainDataObject mdo = new MainDataObject(jobClassName);

            return mdo;
    }   
  }

And I parse json into MainDataObject like:

    BufferedReader br = null;
    try {
         br = new BufferedReader(new FileReader(jobsConfigPath));
    } catch (JsonSyntaxException | JsonIOException | FileNotFoundException e1) {
        LOGGER.error("Exception"+ e1 +"encountered while parsing "+jobsConfigPath);
        throw new RuntimeException("Exception"+ e1 +"encountered while parsing "+jobsConfigPath);
    }
    GsonBuilder gsonBuilder = new GsonBuilder();
    Gson gson = new Gson();     
    gsonBuilder.registerTypeAdapter(MainDataObject.class, new ClassDeserializer()).create();
    MainDataObject  jobList = gson.fromJson(br, MainDataObject.class);

I am not able to parse jobClass into Class object.


Solution

  • You have three mistakes in your code that I can see

    • you are creating a GsonBuilder but then never using it.
    • you are only setting the 'jobClassName' not all the elements of the MainDataObject.
    • The ctor needs a try/catch or to throw an exception.

    .

    import com.google.gson.*;
    import lombok.EqualsAndHashCode;
    import lombok.ToString;
    import org.junit.Test;
    
    import java.lang.reflect.Type;
    
    import static org.hamcrest.MatcherAssert.assertThat;
    import static org.hamcrest.core.Is.is;
    
     public class MeTest {
        @Test
        public void test() {
            GsonBuilder gsonBuilder = new GsonBuilder();
            Gson gson = gsonBuilder.registerTypeAdapter(MainDataObject.class, new ClassDeserializer())
                    .create();
            String json = "{\"jobClass\":\"java.util.AbstractCollection\",\"time\":100,\"tables\":\"tables\"}";
    
    
            MainDataObject data = new MainDataObject("java.util.AbstractCollection");
            data.time = 100;
            data.tables = "tables";
    
            MainDataObject jobList = gson.fromJson(json, MainDataObject.class);
    
            assertThat(jobList, is(data));
        }
    }
    
    class ClassDeserializer implements JsonDeserializer<MainDataObject> {
    
        @Override
        public MainDataObject deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
            String jobClassName = json.getAsJsonObject().get("jobClass").getAsString();
            MainDataObject mdo = new MainDataObject(jobClassName);
            mdo.tables = json.getAsJsonObject().get("tables").getAsString();
            mdo.time = json.getAsJsonObject().get("time").getAsInt();
            return mdo;
        }
    }
    
    // Lombok
    @EqualsAndHashCode @ToString
    class MainDataObject {
        Class<?> jobClass;
        int time;
        String tables;
    
        public MainDataObject(String jobClassname){
            try {
                jobClass =  (Class<?>)Class.forName(jobClassname);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    

    However a better option may be to write a deserializer for the Class type.

    class ClassDeserializer implements JsonDeserializer<Class> {
    
        @Override
        public Class deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
            String jobClassName = json.getAsString();
    
            try {
                return Class.forName(jobClassName);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                return null;
            }
        }
    }