Hector
Hector

Reputation: 5356

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?

Upvotes: 0

Views: 826

Answers (2)

Geo
Geo

Reputation: 756

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);
    }
});

Upvotes: 1

Geo
Geo

Reputation: 756

try using @SerializedName annotation

@SerializedName("abstract")  
private String _abstract;

this annotation comes under google gson annotations package, so may need to add gson dependancy

Upvotes: 1

Related Questions