19

My goal is to persist an object instantiated with GSON to the database with realm.

My problem is the following:

  • I can instantiate an object with GSON, so GSON takes care of all the properties, but then it won't be persisted to db

  • I can instantiate an object with Realm, but then I have to fill in all the setters for the properties.

In my oncreate, this part is the same for both methods

//delete previous realm file
Realm.deleteRealmFile(this);

//gson instance
Gson gson = new Gson();

//realm instance
realm = Realm.getInstance(this);

** The name of my class is Vacature **

Info: I am aware that the following code needs to be inside:

realm.beginTransaction();

// code here

realm.commitTransaction();

Option 1:

//get class with gson
Vacature vacatureGson = gson.fromJson(jsonString, Vacature.class);

This won't work because you have to instantiate a realm object by using

Class instance = realm.createObject(Class.class); // Create a new object

Option 2:

//get instance with gson
Vacature vacatureGson = gson.fromJson(jsonString, Vacature.class);
//make instance with realm
Vacature realmVacature = realm.createObject(Vacature.class);
realmVacature = vacatureGson;

This won't work because this is not a proper way to copy all the properties from one object to another. If there is however a good way to copy all the properties from the gsonObject to the realmObject, option 2 might work.

Any ideas on how to solve this puzzle?

durron597
  • 31,968
  • 17
  • 99
  • 158
TomCB
  • 3,983
  • 9
  • 40
  • 66

3 Answers3

13

Emanuele from Realm here.

Right now the solution is suboptimal and is presented in one of the examples: https://github.com/realm/realm-java/blob/master/examples/gridViewExample/src/main/java/io/realm/examples/realmgridview/GridViewExampleActivity.java

We're working to make the experience way smoother!

Emanuelez
  • 1,651
  • 10
  • 12
  • 1
    I was looking into that example :) you're doing a great job so far! If I understand correctly, in this example you set the setters manually. In the meantime, I'm searching the internet for a solid deepcopy-all-the-properties solution :) – TomCB Oct 09 '14 at 10:05
  • Please keep us updated about this issue. If you can't find a solution, let the community know! Together we can work it out! – TomCB Oct 13 '14 at 10:58
  • 2
    I have a solid idea on how to solve this. I will try to develop a proof of concept as soon as possible. – Emanuelez Oct 13 '14 at 12:18
  • 2
    Hi @Emanuelez , just wondering if there's any update on this? Or is there anywhere I can check for updates? Thanks! – derpyderp Nov 07 '14 at 15:14
  • JSON is a very hot topic for us right now. You can see progress in [this PR](https://github.com/realm/realm-java/pull/489) and [this document](https://github.com/cmelchior/realm-java/blob/cm-json-import-api/json_api.md) – Emanuelez Nov 13 '14 at 10:10
  • @Shatazone As you can see from the PR linked above, we now support a richer feature-set when it comes to importing JSON. It's still not perfect but way better than what we used to have in October-November. See some of our docs [here](http://realm.io/docs/java/0.80.0/#json) and [here](http://realm.io/docs/java/0.80.0/#other-libraries) – Emanuelez Mar 30 '15 at 13:28
  • All Links are broken! – BillHaggerty May 01 '15 at 13:58
  • Thank you @Theyouthis. I fixed the link in my answer – Emanuelez May 04 '15 at 09:14
  • @TomCB how can i update model class object to realm from json response without writing setters manually ? – Harshal Bhatt Jun 15 '15 at 06:09
2

As it was said in previous answers for serialize RealmObject with Gson the appropriate JsonSerializer should be registered at Gson instance. Below my example of an universal JsonSerializer for Gson that works with getters instead of fields.

Firstly, it should be registered with Gson in order to use like this:

    @NonNull private static Gson createGson() {
            GsonBuilder gsonBuilder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation();
            try {        
                    gsonBuilder.registerTypeAdapter(Class.forName("io.realm.<YourClassName1>RealmProxy"), Serializer.newInstance(<YourClassName1>.class));
                    gsonBuilder.registerTypeAdapter(Class.forName("io.realm.<YourClassName2>RealmProxy"), Serializer.newInstance(<YourClassName2>.class));
                    ...
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }

        return gsonBuilder.create();
    }

And then it is possible to get JSON from any RealmObject-derived (and even created by Realm) class like this:

public class MessageModel extends RealmObject { ... }
String json = createGson().toJson(messageModelInstance);

And you can copy MessageModel like this:

MessageModel src= ...; 
MessageModel dst= ...;

Serializer.newInstance(MessageModel.class).copy(src, dst);

And here Serializer

public class Serializer<T> implements JsonSerializer<T> {

    private Class<T> clazz;
    private Serializer(Class<T> clazz) {this.clazz = clazz;}

    @NonNull
    public static <T> Serializer<T> newInstance(Class<T> clazz) {return new Serializer<T>(clazz);}

    @Override
    public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context) {

        final JsonObject jsonObject = new JsonObject();

        Field[] fields = clazz.getDeclaredFields();
        for (Field f : fields) {
            if (f.isAnnotationPresent(Expose.class)) try {

                String name = f.getName();
                String methodName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
                Method method = src.getClass().getMethod(methodName);
                Object o = method.invoke(src);

                if (o instanceof String) {
                    jsonObject.addProperty(name, (String) o);
                }
                else if (o instanceof Number) {
                    jsonObject.addProperty(name, (Number) o);
                }
                else if (o instanceof Boolean) {
                    jsonObject.addProperty(name, (Boolean) o);
                }
                else if (o instanceof Character) {
                    jsonObject.addProperty(name, (Character) o);
                }

            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        return jsonObject;
    }

    public <T> void copy(T src, T dst) {

        Field[] fields = clazz.getDeclaredFields();
        for (Field f : fields) {
            if (f.isAnnotationPresent(Expose.class)) try {

                String name = f.getName();

                String getterName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
                String setterName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
                Method getter = clazz.getMethod(getterName);
                Method setter = clazz.getMethod(setterName, f.getType());

                setter.invoke(dst, getter.invoke(src));

            } catch (Exception e) {
                e.printStackTrace();
                checkState(false);
            }
        }
    }
}
Alexander Skvortsov
  • 2,696
  • 2
  • 17
  • 31
  • what is checkstate?? – Olayinka Jan 20 '16 at 21:36
  • Sorry, what do you mean? Can you explain your question please? – Alexander Skvortsov Jan 22 '16 at 15:54
  • This works great. Another thing to keep in mind is that this will only work if called in the same Thread the RealmObject are created otherwise it will throw Realm exceptions. Also one can remove if (f.isAnnotationPresent(Expose.class)) if your model classes do not use this annotation. – velval Sep 08 '16 at 07:41
0

This can be achieved with ONE LINE OF CODE.

Get a copy in memory of the managed RealmObject and pass it to Gson

String jsonVacature = new Gson().toJson(realm.copyFromRealm(vacature));

Check this answer for more details and for other solutions.

Community
  • 1
  • 1