Sqrt-1764
Sqrt-1764

Reputation: 371

Conversion of a List of HashMaps from/to JSON fails with Moshi 1.2.0

I want to migrate my code from GSon to MOSHI in order to get the benefits of the common underlying usage of the OK-Libraries as I am using this also with OKHTTP and Retrofit.

But a task that is simple with Gson seems to be complicated with MOSHI:

I have a class that contains a list of objects.

And these objects consist of fieldname/value pairs - I implemeted that as a HashMap. In this class there are some more constructors and methods, but for JSON only the field/value-pairs are relevant.

Stripped down to the bare minimum, my JSON should look like:

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

When I try to convert these classes to JSON with MOSHI and back, the children are empty.

Conversion to JSON gives

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

And deserialisation of the json-string from above to Class2 gives 2 children, but the children are emppty.

In my real code I that parent-object also contains lists of objects of other classes - those classes work as expected. The problem here seems to be that my child-class extends from HashMap.

With Gson everything works as expected.

Here is the Unit-Test, i wrote to test the behavior.

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

Upvotes: 4

Views: 1911

Answers (1)

Sqrt-1764
Sqrt-1764

Reputation: 371

After some sleep I found a solution (although I think that Moshi should handle this case out of the box):

As you can read here in the answers, Moshi correctly handles the Map<> interface. The solution is to provide Custom Type Adapter that maps the class to the Map-Interface and back. The rest is then handled by Moshi.

The Code from my question has to be changed as follows: Create an adapter-class that maps to the Map-Interface as described in the documentation of 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.
}

and this adapter has to be added to the moshi-object

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

Now the conversion from and to JSON works as expected.

Upvotes: 1

Related Questions