guochaiqi / google-gson

Automatically exported from code.google.com/p/google-gson
0 stars 0 forks source link

Nested generic collections don't return jsonized values on toJson #218

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
+  private static class ClassWithDistinguishableFieldName {
+    private String distinguishableFieldName = "distinguishableFieldValue";
+  }
+
+  private static class ClassWithHashMapField {
+    private String a = "a";
+    private String b = "b";
+    HashMap<String, Object> c = new HashMap<String, Object>();
+
+    ClassWithHashMapField() {
+      c.put("someArray", Arrays.asList(new 
ClassWithDistinguishableFieldName()));
+    }
+  }

+    ClassWithHashMapField o = new ClassWithHashMapField();
+    String json = gson.toJson(o);

json:
  {"a":"a","b":"b","c":{"someArray":{}}}

expected:
  {"a":"a","b":"b","c":{"someArray":[{"distinguishableFieldName":"distinguishableFieldValue"}]}}

This issue was blocking a project I am working on; so I attached a patch with a 
test.

Original issue reported on code.google.com by masahji%...@gtempaccount.com on 28 Jun 2010 at 2:47

GoogleCodeExporter commented 9 years ago
Attaching patch

Original comment by masahji%...@gtempaccount.com on 28 Jun 2010 at 2:47

Attachments:

GoogleCodeExporter commented 9 years ago
ping

Original comment by masahji%...@gtempaccount.com on 13 Sep 2010 at 12:36

GoogleCodeExporter commented 9 years ago
Same problem here:

public class ComplexObj {
    public List<Map<String, Object>> rows;
}
@Test
public void ttt() {
    List<Map<String, Object>> rows = buildRows(100);
    String json1 = new Gson().toJson(rows);
    ComplexObj complexObj = new ComplexObj();
    complexObj.rows = rows;
    String json2 = new Gson().toJson(complexObj);
    assertThat(json2, containsString(json1));
}
private List<Map<String, Object>> buildRows(int id) {
    List<Map<String, Object>> rows = Lists.newArrayList();
    Map<String, Object> row = Maps.newHashMap();
    row.put("id", id);
    rows.add(row);
    row = Maps.newHashMap();
    row.put("id", 999);
    rows.add(row);
    return rows;
}

I'm also blocked by this issue

Original comment by gianmarco.gherardi on 13 Sep 2010 at 8:28

GoogleCodeExporter commented 9 years ago
Can you avoid using Object as the value in your Map? Use of Object omits type 
information that GSON uses when inspecting your types.

Original comment by limpbizkit on 6 Oct 2010 at 5:51

GoogleCodeExporter commented 9 years ago
I thought my attached patch would fix the problem. There should also be a unit 
test in there as well.

Original comment by masahji%...@gtempaccount.com on 6 Oct 2010 at 5:54

GoogleCodeExporter commented 9 years ago
Issue 234 has been merged into this issue.

Original comment by limpbizkit on 6 Oct 2010 at 6:08

GoogleCodeExporter commented 9 years ago
Gson is able to serialize a plain List<Map<String, Object>>, don't understand 
why it fail when serializing the same type in a custom object

Original comment by gianmarco.gherardi on 6 Oct 2010 at 6:24

GoogleCodeExporter commented 9 years ago
The core problem is that GSON does something slightly differently depending on 
whether it has static type information. When it has no static type information, 
it will use the runtime type. When it has some static type information (as is 
the case when it's serializing the field of a custom object), it will use that 
type information.

For predictable results, always supply GSON will full type information. In this 
case the fix is to replace List<Map<String, Object>> with List<Map<String, 
Foo>>, for the appropriate value of Foo. Don't forget that you can register a 
type adapter for Foo if you need to handle multiple subclasses.

Original comment by limpbizkit on 3 Nov 2010 at 4:31

GoogleCodeExporter commented 9 years ago
I would appreciate a fix on this.  I have a case where I could have a 
List<Map<String, Object>> because the type ending up as the value in the Map 
could be a String, Integer, Boolean, List, or Map.  As I understand the 
description of the reason why this will not be fixed I would need to extend 
each of those classes so I could then register a type adapter to handle 
subclasses of what are simply basic types.

It would be very helpful if this issue could be upgrade from WontFix.  This is 
effectively a blocking issue for me.

Original comment by johnwhea...@gmail.com on 1 Dec 2010 at 5:52

GoogleCodeExporter commented 9 years ago
I need to clarify my nesting.  The nesting I am trying to handle is...

Map<String, Object> where "object" could possibly be a List<Map<String, 
Object>>.

I Map<String, Object> that Object could be an Integer, List, String, Boolean, 
etc.

I have tried updating the list to be a List<Map<String, String>> but that made 
no difference.

Original comment by johnwhea...@gmail.com on 1 Dec 2010 at 6:17

GoogleCodeExporter commented 9 years ago
I had exactly the same situation you do and could not get it to work either.

I instead opted to use FlexJSon -- which I found just as easy to use as
GSon, but which actually could be told to do precisely what you are trying
to accomplish.
http://flexjson.sourceforge.net/

ie:
    JSONSerializer jsonSerializer = new JSONSerializer();
      String jsonString = jsonSerializer.deepSerialize(
myObjectThatMapsAStringToAnObject );

Original comment by brandon....@gmail.com on 1 Dec 2010 at 6:35

GoogleCodeExporter commented 9 years ago
Nifty!  Thanks!  I will look into that.

Original comment by johnwhea...@gmail.com on 1 Dec 2010 at 7:02

GoogleCodeExporter commented 9 years ago
Can this be a solution?

--------------------------------------------------------------------------------
-------------------------------
package com.github.gimmi.spikegson;

import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import org.junit.Test;

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

import static org.junit.Assert.assertEquals;

public class StringTemplateBuilderTest {
    private static class CustomType {
        public String value = "a value";
    }

    @Test
    public void should_serialize_custom_type_in_map_with_type_token() {
        Map<String, Object> obj = new HashMap<String, Object>();
        obj.put("k1", new CustomType());

        Type type = new TypeToken<Map<String, Object>>() {
        }.getType();

        String actual = new Gson().toJson(obj, type).replace('"', '\'');

        assertEquals("{'k1':{}}", actual);

        actual = new GsonBuilder().registerTypeAdapter(Object.class, new JsonSerializer<Object>() {
            @Override
            public JsonElement serialize(Object src, Type typeOfSrc, JsonSerializationContext context) {
                return context.serialize(src);
            }
        }).create().toJson(obj, type).replace('"', '\'');

        assertEquals("{'k1':{'value':'a value'}}", actual);
    }
}
--------------------------------------------------------------------------------
-------------------------------

Original comment by gianmarco.gherardi on 13 Apr 2011 at 5:43

GoogleCodeExporter commented 9 years ago
Decision which accepted by developers, to use the "static information about the 
type of", generate extremely delusional behavior of Gson:

----
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.Arrays;
import java.util.List;

public class Test {
  public static void main(String args[]) {
    Result result = new Result();
    Gson gson = new GsonBuilder().setPrettyPrinting().create();
    gson.toJson(result, System.out);
  }

  interface Column {
  }

  static class ColumnImpl implements Column {
    private final String one;
    private final int two;  
    private final Boolean three;

    public ColumnImpl(String one, int two, Boolean three) {
      this.one = one;
      this.two = two;
      this.three = three;
    }
  }

  static class Result {
    private List<ColumnImpl> columnImpls = Arrays.asList(
      new ColumnImpl("one", 2, false), 
      new ColumnImpl("two", 4, true), 
      new ColumnImpl("three", 8, false));
    private List<Column> columns = (List)columnImpls;
  }
}
------

print:

------
{
  "columnImpls": [
    {
      "one": "one",
      "two": 2,
      "three": false
    },
    {
      "one": "two",
      "two": 4,
      "three": true
    },
    {
      "one": "three",
      "two": 8,
      "three": false
    }
  ],
  "columns": [
    {},
    {},
    {}
  ]
}
------

For what has been chosen such behavior?

Original comment by radio...@ya.ru on 28 Jul 2011 at 11:30