2016-09-16 1 views
2

Je souhaite migrer mon code de GSon vers MOSHI afin d'obtenir les avantages de l'utilisation sous-jacente commune des bibliothèques OK, car j'utilise cette méthode. aussi avec OKHTTP et Retrofit.La conversion d'une liste de HashMaps de/vers JSON échoue avec Moshi 1.2.0

Mais une tâche qui est simple avec Gson semble être compliqué avec Moshi

J'ai une classe qui contient une liste d'objets.

Et ces objets sont constitués de paires nom/valeur de champ - j'ai implémé cela en tant que HashMap. Dans cette classe, il y a plus de constructeurs et de méthodes, mais pour JSON, seules les paires de champs/valeurs sont pertinentes.

Minimaliste au strict minimum, mon JSON devrait ressembler à:

{"children":[{"f1":"v11","f2":"v12"},{"f1":"v21","f2":"v22"}]} 

Lorsque je tente de convertir ces classes JSON avec MOSHI et le dos, les enfants sont vides.

Conversion en JSON donne

{"children":[{},{}]} 

Et désérialisation du JSON-string en haut à Classe2 donne 2 enfants, mais les enfants sont emppty.

Dans mon code réel I cet objet-parent contient également des listes d'objets d'autres classes - ces classes fonctionnent comme prévu. Le problème ici semble être que ma classe d'enfants s'étend de HashMap.

Avec Gson, tout fonctionne comme prévu.

Voici l'Unit-Test, j'ai écrit pour tester le comportement.

public class Test_Moshi { 
    private final Moshi moshi = new Moshi.Builder().build(); 


    private static class Class1 extends HashMap<String, Object> { 
     //Some Constructors and methods omitted for the test. 
     //Relevant for the serilisation to JSON are only the keys and values in the map. 
    } 

    private static class Class2 { 
     List<Class1> children = new ArrayList<>(); 
    } 


    @Test public void test1() { 
     Class1 child; 
     Class2 parent = new Class2(); 

     child = new Class1(); 
     child.put("f1", "v11"); 
     child.put("f2", "v12"); 
     parent.children.add(child); 

     child = new Class1(); 
     child.put("f1", "v21"); 
     child.put("f2", "v22"); 
     parent.children.add(child); 

     String json_gson = new Gson().toJson(parent); 
     String json_moshi = moshi.adapter(Class2.class).toJson(parent); 

     assertEquals(json_gson, json_moshi); 
    } 

    @Test public void test2() throws IOException { 
     String json = "{\"children\":[{\"f1\":\"v11\",\"f2\":\"v12\"},{\"f1\":\"v21\",\"f2\":\"v22\"}]}"; 
     Class2 class2 = moshi.adapter(Class2.class).fromJson(json); 

     assertEquals(2, class2.children.size()); 
     assertEquals("Child 1 contains expected number of fields", 2, class2.children.get(0).size()); 
     assertEquals("Child 2 contains expected number of fields", 2, class2.children.get(1).size()); 
    } 
} 

Répondre

1

Après dormir, j'ai trouvé une solution (même si je pense que Moshi doit gérer ce cas de la boîte):

Comme vous pouvez le lire ici dans les réponses, Moshi gère correctement la carte <> interface. La solution consiste à fournir un adaptateur de type personnalisé qui mappe la classe à l'interface de carte et vice versa. Le reste est ensuite géré par Moshi.

Le code de ma question doit être modifié comme suit: Créez une classe d'adaptateur mappée à l'interface de carte comme décrit dans la documentation de Moshi.

private static class Class1 extends HashMap<String, Object> { 
    public static class class1ToJsonAdapter { 
     @ToJson 
     public Map<String, Object> toJson(Class1 dat) { 
      return (Map<String,Object>)dat; 
     } 

     @FromJson 
     public Class1 fromJson(Map<String,Object> json) { 
      Class1 result = new Class1(); 
      for (String key : json.keySet()) 
       result.put(key, json.get(key)); 
      return result; 
     } 
    } 

    //Some Constructors and methods omitted for the test. 
    //Relevant for the serilisation to JSON are only the keys and values in the map. 
} 

et cet adaptateur doit être ajouté à la Moshi-objet

private final Moshi moshi = new Moshi.Builder() 
     .add(new Class1.class1ToJsonAdapter()) 
     .build(); 

Maintenant, la conversion et de JSON fonctionne comme prévu.