Gson如何将List<String>反序列化为RealmList<RealmString>。

26

我正在使用retrofit和gson将我的json反序列化为realm对象。这在大多数情况下都很好用。但是当涉及到

RealmList(String(或任何其他基本数据类型))

由于Realm不支持E不扩展Realm对象的RealmList,所以我将String包装在RealmObject中。

public class RealmString extends RealmObject {
  private String val;

  public String getValue() {
    return val;
  }

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

我的 realm 对象如下所示

    public class RealmPerson extends RealmObject {
    @PrimaryKey
    private String userId;
    ...
    private RealmList<RealmString> stringStuff;
    private RealmList<SimpleRealmObj> otherStuff;

    <setters and getters>
   }

SimpleRealmObj 只包含 String 元素,因此它能很好地运行。

    public class SimpleRealmObj extends RealmObject {
    private String foo;
    private String bar;
       ...
    }

我该如何反序列化stringStuff?我尝试使用gson TypeAdapter,但没成功。

public class RealmPersonAdapter extends TypeAdapter<RealmPerson> {
    @Override
    public void write(JsonWriter out, RealmPerson value) throws IOException {
        out.beginObject();
        Log.e("DBG " + value.getLastName(), "");
        out.endObject();
    }

    @Override
    public RealmPerson read(JsonReader in) throws IOException {
        QLRealmPerson rList = new RealmPerson();
        in.beginObject();
        while (in.hasNext()) {
            Log.e("DBG " + in.nextString(), "");
        }
        in.endObject();

        return rList;
    }

然而,我仍然遇到了IllegalStateException异常。

2334-2334/com.qualcomm.qlearn.app E//PersonService.java:71﹕ main com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was NAME at line 1 column 3 path $.

我之前尝试使用RealmList和RealmString适配器来解决问题,但都没有成功。 目前我找到的唯一解决方法是https://github.com/realm/realm-java/issues/620#issuecomment-66640786 有更好的解决方案吗?

4个回答

33

使用 JsonSerializerJsonDeserializer要比使用TypeAdapter更好,因为有两个原因:

  1. 它们允许您将序列化和反序列化委托给默认的Gson(反)序列化程序,这意味着您不需要自己编写样板代码。

  2. 在Gson 2.3.1中存在奇怪的错误可能会导致反序列化期间出现StackOverflowError(我尝试了TypeAdapter方法,并遇到了这个错误)。

以下是如何做到这一点(将Tag替换为您的RealmObject类):

用于RealmList<Tag>的解析器+序列化器:

public class TagRealmListConverter implements JsonSerializer<RealmList<Tag>>,
        JsonDeserializer<RealmList<Tag>> {

    @Override
    public JsonElement serialize(RealmList<Tag> src, Type typeOfSrc,
                                 JsonSerializationContext context) {
        JsonArray ja = new JsonArray();
        for (Tag tag : src) {
            ja.add(context.serialize(tag));
        }
        return ja;
    }

    @Override
    public RealmList<Tag> deserialize(JsonElement json, Type typeOfT,
                                      JsonDeserializationContext context)
            throws JsonParseException {
        RealmList<Tag> tags = new RealmList<>();
        JsonArray ja = json.getAsJsonArray();
        for (JsonElement je : ja) {
            tags.add((Tag) context.deserialize(je, Tag.class));
        }
        return tags;
    }

}

标签类:

@RealmClass
public class Tag extends RealmObject {
    private String value;

    public String getValue() {
        return value;
    }

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

然后使用Gson注册您的转换器类:

Gson gson = new GsonBuilder()
        .registerTypeAdapter(new TypeToken<RealmList<Tag>>() {}.getType(),
                new TagRealmListConverter())
        .create();

你的解决方案是最好的! - suyanlu
这个解决方案是最好的,而且没有其他解决方案中推荐的样板代码。 - Arthur
2
对我来说仍然无法工作:在 tags.add((RealmString) context.deserialize(je, RealmString.class)) 中,出现了 java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING - Henrique de Sousa
@VickyChijwani 是否可以使用动态类型的同一类来处理所有的RealmLists?例如<RealmList<T>>。 - Siarhei
@user599807 我不确定,但是快速查看后,我认为这应该是可能的。 - Vicky Chijwani
显示剩余8条评论

5
"Expected a string but was NAME"的错误信息可以通过在实际json对象之前(在你的情况下它是一个String)在JsonReader中检索json对象的名称来解决。 您可以查看Android JsonReader文档,其中详细解释了代码片段,您还可以查看文档中示例代码中的readMessage方法。 我已经修改了您的read方法,以符合我的想法。 注意:我没有测试过代码,因此可能会有一些小错误。
@Override
public RealmPerson read(JsonReader in) throws IOException {
    RealmPerson rList = new RealmPerson();
    in.beginObject();
    String name = "";
    while (in.hasNext()) {
        name = in.nextName();

        if (name.equals("userId")) {
            String userId = in.nextString();
            // update rList here 
        } else if (name.equals("otherStuff")) {
            // since otherStuff is a RealmList of RealmStrings,
            // your json data would be an array
            // You would need to loop through the array to retrieve 
            // the json objects
            in.beginArray();
            while (in.hasNext()) {
                // begin each object in the array
                in.beginObject();
                name = in.nextName();
                // the RealmString object has just one property called "value"
                // (according to the code snippet in your question)
                if (name.equals("val")) {
                    String val = in.nextString();
                     // update rList here 
                } else {
                    in.skipValue();
                }
                in.endObject();
            }
            in.endArray();
        } else {
            in.skipValue();
        }
    }
    in.endObject();


    return rList;
}

如果这篇文章对你有所帮助,请告诉我。


1
感谢@iRuth。这确实是反序列化JSON blob并回答原始问题的方法。我将把它标记为正确答案。 对于任何只想反序列化realmString的人,请查看我的下面的答案。 - avid

4
我的gson typeAdapter是罪魁祸首。上述错误是由于我没有正确将json反序列化为RealmPerson,第一个字段不是字符串,因此

in.nextString()

出现问题。

我查看了一些示例代码,发现我不必使用

in.beginObject()和in.endObject()

来反序列化字符串。以下代码有效。

public class QLRealmStringAdapter extends TypeAdapter<QLRealmString> {
@Override
public void write(JsonWriter out, QLRealmString value) throws IOException {
    Log.e("DBG " + value.getValue(), "");
    out.value(value.getValue());
}

@Override
public RealmString read(JsonReader in) throws IOException {
    RealmString rString = new RealmString();
    if (in.hasNext()) {
        String nextStr = in.nextString();
        System.out.println("DBG " + nextStr);
        rString.setValue(nextStr);
    }

    return rString;
}

希望这对某些人有所帮助。


这就是缺失的部分!我将所有模型转换为使用原始类型和RealmString对象,设置了一个类似于此处所见的排除策略:https://dev59.com/Eo7ea4cB1Zd3GeqPGtpQ#32891674 ,然后实现了这个RealmString TypeAdapter,一切都完美地运作了!谢谢 - speedynomads

0
我需要一个Jackson序列化器和反序列化器,用于将ArrayList转换为RealmList。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接