web-dev-qa-db-fra.com

Désérialisation Gson de List <String> dans realmList <RealmString>

J'utilise retrofit avec gson pour désérialiser mon json en objets du royaume. Cela fonctionne très bien pour la plupart. Des problèmes surgissent lorsqu'il s'agit de 

RealmList (String (ou tout autre type de données de base))

Etant donné que Realm ne prend pas en charge RealmList où E n’étend pas l’objet Realm, j’enveloppe String dans un RealmObject.

public class RealmString extends RealmObject {
  private String val;

  public String getValue() {
    return val;
  }

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

Mon objet de royaume est comme ci-dessous

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

    <setters and getters>
   }

SimpleRealmObj fonctionne bien car il ne contient que des éléments String

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

Comment puis-je désérialiser stringStuff? J'ai essayé d'utiliser un 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;
    }

Cependant, je frappe toujours à IllegalStateException

2334-2334/com.qualcomm.qlearn.app E // PersonService.Java: 71 main com.google.gson.JsonSyntaxException: Java.lang.IllegalStateException: chaîne attendue, mais NOM à la ligne 1 colonne 3 chemin $.

J'ai essayé RealmList, l'adaptateur RealmString plus tôt en vain .. La seule solution de rechange que j'ai réussi à trouver jusqu'à présent est https://github.com/realm/realm-Java/issues/620#issuecomment-66640786 De meilleures options?

25
avid

Le message d'erreur "Expected a string but was NAME" peut être résolu en récupérant le nom de l'objet json dans la JsonReader avant l'objet json (qui est une String dans votre cas). 

Vous pouvez consulter la documentation sur Android pour JsonReader . Il a l'explication détaillée et l'extrait de code. Vous pouvez également consulter la méthode readMessage dans l'exemple de code extrait de la documentation.

J'ai modifié votre méthode read à ce que je pense qu'elle devrait être. NOTE: Je n'ai pas testé le code, il peut donc y avoir des erreurs mineures.

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

Faites-moi savoir si cela aide.

5
iRuth

Il est préférable d'utiliser JsonSerializer et JsonDeserializer plutôt que TypeAdapter pour votre RealmObject, pour deux raisons:

  1. Ils vous permettent de déléguer la (dé) sérialisation de votre RealmObject au sérialiseur Gson (de) par défaut, ce qui signifie que vous n'avez pas besoin d'écrire le passe-passe vous-même.

  2. Il y a un bogue étrange dans Gson 2.3.1 qui pourrait causer un StackOverflowError pendant la désérialisation (j'ai essayé l'approche TypeAdapter moi-même et j'ai rencontré ce bogue).

Voici comment (remplacez Tag par votre classe RealmObject):

(NOTE que context.serialize et context.deserialize ci-dessous sont équivalents à gson.toJson et gson.fromJson, ce qui signifie que nous n'avons pas besoin d'analyser la classe Tag nous-mêmes.)

Analyseur + sérialiseur pour 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;
    }

}

Classe de tag:

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

    public String getValue() {
        return value;
    }

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

Puis enregistrez votre classe de convertisseur avec Gson:

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

Mon gson typeAdapter était le coupable . L'erreur ci-dessus a été vue car je ne désérialisais pas correctement le json dans RealmPerson, le premier champ n'est pas une chaîne, d'où

in.nextString ()

était borking. 

J'ai regardé un exemple de code et cela m'a frappé, je n'ai pas eu à utiliser

in.beginObject () et in.endObject ()

désérialiser une chaîne. Le code ci-dessous fonctionne.

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

}

J'espère que ça aide quelqu'un.

4
avid

j'ai besoin d'un sérialiseur et d'un désérialiseur jackson pour la conversion d'arrayliste en liste de domaines

0
avez raj