2

Due to Realm inability to work with promotive types, which include Strings, I'm trying to implement a JsonDeserializer just like in this question.

The issue is that I'm baffled on to why I'm getting the following error:

W/System.err: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING

This is part of the Json: "tags": ["GLUTEN FREE", "NUT FREE"],

My RealmString:

public class RealmString extends RealmObject {
    private String value;

    public RealmString() {
    }

    public RealmString(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

Part of the retrofited Pojo:

public class Entity extends RealmObject {

    @SerializedName("tags")
    private RealmList<RealmString> tags = null;
}

.. and the Deserializer:

public class StringRealmListConverter implements JsonDeserializer<RealmList<RealmString>> {

    @Override
    public RealmList<RealmString> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
            throws JsonParseException {

        RealmList<RealmString> realmStrings = new RealmList<>();
        JsonArray ja = json.getAsJsonArray();
        for (JsonElement je : ja) {
            realmStrings.add((RealmString) context.deserialize(je, RealmString.class));
        }

        return realmStrings;
    }
}

And I'm registering it here:

public Gson provideGson() {
    return new GsonBuilder()
            .registerTypeAdapter(Food.class, new FoodDeserializer())
            .registerTypeAdapter(new TypeToken<RealmList<RealmString>>() {}.getType(),
                    new StringRealmListConverter())
            .create();
}

edit Here's the FoodDeserializer. It's in this mess because we had to use Composition over Inheritance in order to please the Realm gods:

public class FoodDeserializer implements JsonDeserializer<Food> {
    public static final String TAG = FoodDeserializer.class.getSimpleName();

    Gson mHelperGson;

    public FoodDeserializer() {
        mHelperGson = new GsonBuilder().create();
    }

    @Override
    public Food deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        String type = json.getAsJsonObject().get("responseType").getAsString();
        switch (type) {
            case "Platter":
                return parsePlatter(json);
            case "FoodItem":
                return parseFoodItem(json);
            default:
                return null;
        }
    }

    private PlatterEntity parsePlatter(JsonElement json) {

        FoodEntity food = mHelperGson.fromJson(json, new TypeToken<FoodEntity>() {
        }.getType());

        ArrayList<FoodItemEntity> items = new ArrayList<>();
        JsonElement je1 = json.getAsJsonObject().get("items");
        if (je1 != null) {
            JsonArray list = je1.getAsJsonArray();
            if (list != null) {
                items = mHelperGson.fromJson(list.toString(), new TypeToken<List<FoodItemEntity>>() {
                }.getType());
            }
        }

        return new PlatterEntity(food, items);
    }

    private FoodItemEntity parseFoodItem(JsonElement json) {

        FoodEntity food = mHelperGson.fromJson(json, new TypeToken<FoodEntity>() {
        }.getType());

        Boolean readyToEat = null;
        JsonElement je1 = json.getAsJsonObject().get("readyToEat");
        if (je1 != null) {
            readyToEat = je1.getAsBoolean();
        }

        String heatingInstructions = null;
        JsonElement je2 = json.getAsJsonObject().get("heatingInstructions");
        if (je2 != null) {
            heatingInstructions = je2.getAsString();
        }

        ArrayList<IngredientEntity> ingredients = new ArrayList<>();
        JsonElement je3 = json.getAsJsonObject().get("ingredients");
        if (je3 != null) {
            JsonArray list = je3.getAsJsonArray();
            if (list != null) {
                ingredients = mHelperGson.fromJson(list.toString(), new TypeToken<List<IngredientEntity>>() {
                }.getType());
            }
        }

        NutritionEntity foodNutritions = mHelperGson.fromJson(json, new TypeToken<NutritionEntity>() {
        }.getType());

        return new FoodItemEntity(food, readyToEat, heatingInstructions, ingredients, foodNutritions);
    }
}

I would like to use a JsonDeserializer over TypeAdapter, but any help will be appreciated, thanks!

Community
  • 1
  • 1
Henrique de Sousa
  • 5,727
  • 49
  • 55
  • 1
    `realmStrings.add((RealmString) context.deserialize(je, RealmString.class));` why? ... you can use `realmStrings.add(new RealmString(je.getAsString()));` – Selvin Jun 09 '16 at 13:33
  • or use this answer http://stackoverflow.com/questions/29493101/gson-deserialization-for-realm-list-of-primitives (remeber to change int to string) – Selvin Jun 09 '16 at 13:36
  • You can't deserialize Realm list in GSON, if you don't override your own deserializer. – Alex Jun 09 '16 at 13:41
  • @Selvin I've used that answer as a base for this, and still doesn't work. – Henrique de Sousa Jun 09 '16 at 14:31
  • @Alexander I've updated my answer, I think the problem is the RealmString is inside the Food object, which is why it's not trigerring my RealmString deserializer! – Henrique de Sousa Jun 09 '16 at 14:33
  • You're right. Could you post the code for your FoodDeserializer as well? – Vicky Chijwani Jun 09 '16 at 15:25

2 Answers2

0

Turns out my feelings were right. I had to add the StringRealmListConverter to the FoodDeserializable constructor, like this:

    public FoodDeserializer() {
        mHelperGson = new GsonBuilder()
            .registerTypeAdapter(new TypeToken<RealmList<RealmString>>() {
                    }.getType(),
                    new StringRealmListConverter())
            .create();
    }
Henrique de Sousa
  • 5,727
  • 49
  • 55
0

You can use the JsonDeserializationContext (passed as the second parameter to your deserialize function) to deserialize the RealmString correctly. The context knows how to deserialize all your custom types that are registered with the current Gson instance. See the documentation for JsonDeserializationContext here: https://google.github.io/gson/apidocs/com/google/gson/JsonDeserializationContext.html.

The reason it doesn't work with your current code is because you're creating a new Gson instance in FoodDeserializer which doesn't know about the custom deserializer for RealmString.

Vicky Chijwani
  • 10,191
  • 6
  • 56
  • 79