guochaiqi / google-gson

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

Confusing behavior when Object is the field type #205

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?
1. Object[] array = new Object[] { new Object[] { 1, 2 } };
2. Gson gson = new Gson();
3. System.out.println(gson.toJson(array));

What is the expected output? 
[[1,2]]

What do you see instead?
[{}]

What version of the product are you using? On what operating system?
1.4 on Windows XP.

Please provide any additional information below.
I may be doing this wrong...  Is that behavior intentional?

Original issue reported on code.google.com by michael.hixson@gmail.com on 23 Apr 2010 at 10:51

GoogleCodeExporter commented 9 years ago
I add something similar to your problem, issue 209.
I "fix" your problem as following on version 1;3 (1.4 is beta did not look at 
it):
Class: com.google.gson.JsonSerializationVisitor
Method: visitArray

Old content
  public void visitArray(Object array, Type arrayType) {
    assignToRoot(new JsonArray());
    int length = Array.getLength(array);
    TypeInfoArray fieldTypeInfo = TypeInfoFactory.getTypeInfoForArray(arrayType);
    Type componentType = fieldTypeInfo.getSecondLevelType();
    for (int i = 0; i < length; ++i) {
      Object child = Array.get(array, i);
      Type childType = componentType;
      // we should not get more specific component type yet since it is possible
      // that a custom
      // serializer is registered for the componentType
      addAsArrayElement(new ObjectTypePair(child, childType, false));
    }
  }

New content
  public void visitArray(Object array, Type arrayType) {
    assignToRoot(new JsonArray());
    int length = Array.getLength(array);
    for (int i = 0; i < length; ++i) {
      Object child = Array.get(array, i);
      // we should not get more specific component type yet since it is possible
      // that a custom
      // serializer is registered for the componentType
      addAsArrayElement(new ObjectTypePair(child, child.getClass(), false));
    }
  }

Etienne

Original comment by lapinouj...@gmail.com on 15 May 2010 at 1:02

GoogleCodeExporter commented 9 years ago
Sorry my thread was for the current version in trunk.
For version 1.3 I did the folowwing:
Class: com.google.gson.JsonSerializationVisitor
Method: visitArray
Old content
  public void visitArray(Object array, Type arrayType) {
    assignToRoot(new JsonArray());
    int length = Array.getLength(array);
    TypeInfoArray fieldTypeInfo = TypeInfoFactory.getTypeInfoForArray(arrayType);
    Type componentType = fieldTypeInfo.getSecondLevelType();
    for (int i = 0; i < length; ++i) {
      Object child = Array.get(array, i);
      addAsArrayElement(componentType, child);
    }
  }

New content
  public void visitArray(Object array, Type arrayType) {
    assignToRoot(new JsonArray());
    int length = Array.getLength(array);
    for (int i = 0; i < length; ++i) {
      Object child = Array.get(array, i);
      addAsArrayElement(child.getClass(), child);
    }
  }

Etienne

Original comment by lapinouj...@gmail.com on 15 May 2010 at 1:10

GoogleCodeExporter commented 9 years ago

Original comment by inder123 on 3 Nov 2010 at 1:48

GoogleCodeExporter commented 9 years ago
FYI - you can work around this by providing static type information when 
calling toJson().
  toJson(array, Integer[][].class);

In general GSON works better when it knows the types of everything!

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

GoogleCodeExporter commented 9 years ago
@limpbizkit: Understood.  

In the situation that led to this report, those arrays were buried deep within 
a Map<String, Object>, which contained many nested maps, collections, and 
arrays of various types.  I was building this data structure up in Java and 
wanted to convert it to JSON in one pass, so that solution would not have been 
practical for me.  

Original comment by michael.hixson@gmail.com on 3 Nov 2010 at 5:44

GoogleCodeExporter commented 9 years ago
Yup, good to know. One thing we should consider doing is using runtime types 
whenever "Object" is the static type.

Original comment by limpbizkit on 3 Nov 2010 at 5:54

GoogleCodeExporter commented 9 years ago

Original comment by limpbizkit on 4 Nov 2010 at 10:52

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

Original comment by limpbizkit on 4 Nov 2010 at 10:53

GoogleCodeExporter commented 9 years ago
I have an inkling that this works in Gson 1.7.

We have a test for a Collection<Object>, Object[] and a Map<String,Object>.

Original comment by joel.leitch@gmail.com on 13 Apr 2011 at 9:16

GoogleCodeExporter commented 9 years ago
The array of object arrays is actually assigned to an "Object[][]" instead of 
Object[] than it does work as expected.

Marking this as fixed because since it would be a super crazy use case if 
someone actually need to assign an array of object arrays to a type object 
array.

Original comment by joel.leitch@gmail.com on 15 Apr 2011 at 5:19

GoogleCodeExporter commented 9 years ago
I think you've misunderstood the use case and the bug report, and you've marked 
this "fixed" when you meant "closed" or "won't fix".  

  Gson gson = new Gson();
  System.out.println(
    gson.toJson(new Object[] { 1, 2, new Object[] { "foo", 3 } })
  );

Expected output:

  [1,2,["foo",3]]

Actual output:

  [1,2,{}]

Obviously, that code won't compile if you put "new Object[][]" in place of "new 
Object[]".  What type would you suggest instead, to hold "{ 1, 2, new Object[] 
{ "foo", 3 } }" ?

This report didn't come from a contrived use case designed to break Gson.  
Please see comment #5.

Original comment by michael.hixson@gmail.com on 15 Apr 2011 at 5:46

GoogleCodeExporter commented 9 years ago
OK, I see. I did misunderstand your initial example, but the above example 
makes it more clear because now I see you are mixing primitives (or objects) 
and arrays within an array.

Again, I stand by my statement that is is a super crazy use-case to support, 
but I'd like to hear the your reasoning as to why this is wanted. The only 
thing that I can think of is that you are trying to form some object that will 
object the correct JSON value for you. If that's the case then I recommend that 
you build up the output using the JsonElement objects (i.e. JsonArray, 
JsonObject, JsonPrimitive) and generate the output that way.

I will reopen this issue to hear your thoughts.

Original comment by joel.leitch@gmail.com on 16 Apr 2011 at 10:08

GoogleCodeExporter commented 9 years ago
I swear, I'm not crazy! :)

I was passing a large amount of data from a Java-based back end to a 
JavaScript-based front end.  It seemed only natural to form the container for 
this data in Java using maps, arrays/collections, strings, and primitives, 
which have counterparts in JSON.  Then I'd use some Java-to-JSON tool to make 
the data readable by the client.  

The JavaScript didn't know or care about the Java types being used, or whether 
they were being mixed - [ 1, 2, [ "foo", 3 ] ] is an array like any other.  Why 
should I avoid that mixing on the Java side when the JavaScript doesn't care?  
The front end might (does) even encourage it in some cases.  For instance, look 
at the API for Flot:

  http://people.iola.dk/olau/flot/API.txt

Specifically, see the section about gradients.  What I declare as the 
backgroundColor of a graph could be a string, or an array of strings, of an 
array of maps of strings to numbers.  

Why do I need to worry about that on the Java side?  I shouldn't, but with 
Gson, I do.  Even with the weak example in my initial report:

  new Object[] { new Object[] { 1, 2 } }

Who would want that to render in JSON as "[{}]" ?  It seems plain that's not 
the right representation.  If I have to add a second set of square brackets, 
then I'd be doing so only to appease Gson, because of the particulars in how 
Gson was implemented.  

Addressing your suggestion to use JsonElement objects:  I wouldn't want to tie 
myself to a particular Java-to-JSON library in this way.  If my Java object is 
made of things that have an obvious counterpart in JavaScript, then I'd think a 
number of tools could do the JSON conversion for me.  I could choose between 
them based on correctness and performance.  Meanwhile, using Gson's API for 
building the entire structure would marry me to Gson.

Original comment by michael.hixson@gmail.com on 18 Apr 2011 at 1:26

GoogleCodeExporter commented 9 years ago
To paraphrase, you want to build up a JSON parse tree using Java constructs 
instead of using the Gson parse tree constructs. JsonObject --> Map<String, 
Object>, JsonArray --> List<Object> or Object[], JsonPrimitive --> (String, 
int, long, ....), JsonNull ---> null.

We talked about getting rid of the JsonElement parse tree hierarchy and have 
Gson use the common Java classes to map to JSON structures; however, this makes 
writing JsonSerializers and deserializers very difficult and hard to 
understand. That said, Gson should definitely be able to handle this use case 
and it is a bug.

Original comment by joel.leitch@gmail.com on 20 Apr 2011 at 10:22

GoogleCodeExporter commented 9 years ago
Fixed in r828.

Original comment by joel.leitch@gmail.com on 20 Apr 2011 at 10:33

GoogleCodeExporter commented 9 years ago
See the new test added here:
http://code.google.com/p/google-gson/source/browse/trunk/gson/src/test/java/com/
google/gson/functional/ArrayTest.java?r=829#280

FYI, we are planning to launch a version 1.7.2 in the next two weeks that will 
contain only bug fixes and performance enhancements. Stay tuned.

Original comment by joel.leitch@gmail.com on 20 Apr 2011 at 10:44

GoogleCodeExporter commented 9 years ago
Awesome!

Gson can't deserialize its own JSON output for these mixed-type objects, 
correct?  That is, unless the user writes a custom deserializer?  (I'm not 
suggesting it should; I'm wondering about the nature of your changes above.)

Original comment by michael.hixson@gmail.com on 21 Apr 2011 at 12:25