Search code examples
androidjsonrealm-mobile-platform

How to map Json to valid Java field name with Realm


My current Android application employs Realm as the local database.

I am populating my Realm entities with data sourced from third party API's.

These API's produce Json response messages which I am persisting to realm using createAllRealm.createAllFromJson(clazz, json)

One of these API's has a response message as shown below

{
  "content": [
    {
      "abstract": "string",
      "article_id": 0,
      "authors": [
        "string"
      ],
      "doi": "string",
      "ending_page": "string",
      "issue": "string",
      "journal_name": "string",
      "publisher": "string",
      "starting_page": "string",
      "title": "string",
      "volume": "string",
      "year": 0
    }
  ],
  "total_number": 0
}

My Realm database object obviously has to have exactly matching field names to ensure all the response data is persisted, e.g.:-

    @PrimaryKey
    private Long article_id;

    private String authors;
    private Long year;
    private Long ending_page;
    private Long starting_page;
    private String _abstract;
    private String doi;
    private String issue;
    private String journal_name;
    private String publisher;
    private String title;
    private String volume;

As the message contains a Json field name that is a Java keyword e.g. "abstract" I have to name my java field _abstract.

When I persist these messages _abstract is always null as the Json and Java field names do not match.

Does Realm have any way I can resolve this issue and still employ createAllFromJson?

Or will I have to use Jackson or GSON to convert the Json message to Java objects before I can persist the response data?


Solution

  • there is a workaround mentioned in https://github.com/realm/realm-java/issues/1470

    Until this issue is resolved I have made a workaround in my App, let's say I have Chat.java

    public class Chat extends RealmObject {
        @Index
        @SerializedName("c")
        private String code = "";
    
        @Index
        @SerializedName("t")
        private int type;
    }
    

    And RealmUtil.java

    public class RealmUtil {
    
    
        private static HashMap<String, HashMap<String, String>> classesFieldsMap = new HashMap<>();
    
        static {
            initClasses(Chat.class);
        }
    
        private static void initClasses(Class cls) {
            if (!classesFieldsMap.containsKey(cls.toString())) {
                Field[] fields = cls.getDeclaredFields();
                HashMap<String, String> fieldsMap = new HashMap<>();
                for (Field a : fields) {
                    SerializedName annotation = a.getAnnotation(SerializedName.class);
                    if (annotation != null) {
                        fieldsMap.put(annotation.value(), a.getName());
                    }
                }
                classesFieldsMap.put(cls.toString(), fieldsMap);
            }
        }
    
        public static JSONObject mapGsonObjectToRealm(Class cls, JSONObject object) throws JSONException {
            JSONObject newUserObj = new JSONObject();
            HashMap<String, String> fieldsMap = classesFieldsMap.get(cls.toString());
            for (String setKey : fieldsMap.keySet()) {
                String mappedKey = fieldsMap.get(setKey);
                if (object.has(setKey))
                    newUserObj.put(mappedKey, object.get(setKey));
            }
            return newUserObj;
        }
    }
    

    By using reflection initClasses method will map each of the Gson SerializedName of the class to the corresponding names for Realm record.

    And mapGsonObjectToRealm method will create a new object of the old one with new fields names.

    Usage:

    JSONObject newObject = RealmUtil.mapGsonObjectToRealm(Chat.class, new JSONObject().put("c", "this_is_code").put("t", "this_is_type"));
    final JSONArray jsonArray = new JSONArray();
    jsonArray.put(newObject);
    Realm.getDefaultInstance().executeTransactionAsync(new Realm.Transaction() {
        @Override
         public void execute(Realm realm) {
            realm.createOrUpdateAllFromJson(Chat.class, jsonArray);
        }
    });