getting a StackOverflow while trying to serialize domain objects in Grails

5,056 views
Skip to first unread message

Brian Bonner

unread,
May 30, 2012, 9:13:15 AM5/30/12
to googl...@googlegroups.com
I've logged a bug over on the Grails side of things along w/ a test case at http://jira.grails.org/browse/GRAILS-9127?focusedCommentId=71108#comment-71108


Basically, I'm trying to use Grails map based constructors to create objects.  I have a test case where I populate Domain objects using the constructor and when I check the values on the object, they're populated correctly.

For some reason however, my test fails as soon as I try to serialize Operations using Gson.   I'm using Gson 2.2.1.

Can anyone shed some light on why this might be happening?   The code works with Java and Groovy so I would think it would be related to how Grails is constructing objects using the constructor, but I don't seem to see anything wrong there.   It's almost as if it's struggling with the types involved.

Any help would be appreciated.


java.lang.StackOverflowError
at sun.misc.FpUtils.getExponent(FpUtils.java:130)
at java.lang.Math.getExponent(Math.java:1295)
at java.lang.StrictMath.floorOrCeil(StrictMath.java:338)
at java.lang.StrictMath.floor(StrictMath.java:323)
at java.lang.Math.floor(Math.java:407)
at sun.misc.FloatingDecimal.dtoa(FloatingDecimal.java:601)
at sun.misc.FloatingDecimal.<init>(FloatingDecimal.java:440)
at java.lang.Double.toString(Double.java:179)
at java.lang.String.valueOf(String.java:2973)
at java.lang.Double.toString(Double.java:603)
at com.google.gson.stream.JsonWriter.value(JsonWriter.java:480)
at com.google.gson.Gson$4.write(Gson.java:266)
at com.google.gson.Gson$4.write(Gson.java:251)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
at com.google.gson.internal.bind.ObjectTypeAdapter.write(ObjectTypeAdapter.java:105)

Inderjeet Singh

unread,
May 30, 2012, 12:24:02 PM5/30/12
to googl...@googlegroups.com
Gson would throw a StackOverflow if you have circular references. Could that be the reason?

Inder

Brandon Mintern

unread,
May 30, 2012, 2:03:53 PM5/30/12
to googl...@googlegroups.com
If you don't need to de/serialize inner classes, you can configure
your GsonBuilder with disableInnerClassSerialization() to avoid
problems like this.
> --
> You received this message because you are subscribed to the Google Groups
> "google-gson" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/google-gson/-/o0GSwZJQzyEJ.
> To post to this group, send email to googl...@googlegroups.com.
> To unsubscribe from this group, send email to
> google-gson...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/google-gson?hl=en.

Brian Bonner

unread,
May 30, 2012, 5:55:55 PM5/30/12
to googl...@googlegroups.com
nope, no circular references.

Brian Bonner

unread,
May 30, 2012, 6:04:51 PM5/30/12
to googl...@googlegroups.com
Brandon doing this doesn't change the results.  I still get a stack overflow.

Here's my classes and my test:

@EqualsAndHashCode
class Operations {
Operation op1
Operation op2
static constraints = {
op1 bindable:true
op2 bindable:true
}
static embedded = ['op1', 'op2']
}

@EqualsAndHashCode
public class Operation {

String label
double number1
double number2

public Operation() {
super()
}
}


and my test:


@Test
void testMarshallJsonUsingConstructor() {
Operation op1 = new Operation(label: "test1", number1:95.1, number2:84.1 )
Operation op2 = new Operation(label: "test2", number1:85.1, number2:74.1 )

Operations r = new Operations(op1:op1,op2:op2)
assertEquals(op1, r.op1)
assertEquals(op2, r.op2)
Gson g = new GsonBuilder().disableInnerClassSerialization().create()
String json = g.toJson(r)    // this line blows the stack overflow
print json
}

Jesse Wilson

unread,
May 31, 2012, 12:05:30 AM5/31/12
to googl...@googlegroups.com
Could you paste the full stack trace? You've only pasted about 20 stack frames and the VM usually needs far more than that to trigger a stack overflow. I suspect you've got an unexpected circular reference, which should reveal itself in the way your stack frames repeat themselves.

Brian Bonner

unread,
May 31, 2012, 8:55:46 AM5/31/12
to googl...@googlegroups.com
Here's the full stack trace.  Sorry, I thought I cut it off where it was repeating.

java.lang.StackOverflowError
at java.io.StringWriter.write(StringWriter.java:84)
at java.io.StringWriter.append(StringWriter.java:126)
at java.io.StringWriter.append(StringWriter.java:24)
at com.google.gson.stream.JsonWriter.beforeValue(JsonWriter.java:610)
at com.google.gson.stream.JsonWriter.open(JsonWriter.java:317)
at com.google.gson.stream.JsonWriter.beginObject(JsonWriter.java:300)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:190)
at com.google.gson.internal.bind.ObjectTypeAdapter.write(ObjectTypeAdapter.java:105)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)

Brian Bonner

unread,
Jun 2, 2012, 12:32:17 AM6/2/12
to googl...@googlegroups.com
Was the full stack trace helpful at all?

Brian Bonner

unread,
Jun 2, 2012, 4:37:42 PM6/2/12
to googl...@googlegroups.com
I found some more information.   I debugged it and noticed that it started in the circular loop when it hit the errors property (a dynamically added property in grails).  This explains why it doesn't occur when I do it w/ Java and Groovy -- this property doesn't get added.

When it does this, it finds another class property called target and then it goes through the entire class again.

I'm not sure why errors and target does this, but I'm going to try to exclude errors and see if I get farther.

Brian Bonner

unread,
Jun 2, 2012, 4:56:48 PM6/2/12
to googl...@googlegroups.com
I included an Exclusion strategy to remove the errors field from serialization...I'm not sure if this is the best strategy, but it's working for now.

/**
 * This class is used because GSON seems to hate serializing the Grails errors
 * class and gets into an infinite loop. See:
 * 
 * @author bbonner
 * 
 */
public class GrailsDomainExclusionStrategy implements ExclusionStrategy {
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}

@Override
public boolean shouldSkipField(FieldAttributes f) {
if (f.getName().equals("errors")) {
return true;
}
return false;
}

}


I then created a GSON instance as explained in the User Guide:

Gson g = new GsonBuilder().setExclusionStrategies(new GrailsDomainExclusionStrategy()).create()
String json = g.toJson(r)


I'm not sure if this is necessary going to give me what I want, but it definitely removes the circular reference.

Here's the resulting JSON:
{"op1":{"label":"test1","number1":95.1,"number2":84.1},"op2":{"label":"test2","number1":85.1,"number2":74.1}}

If I alternatively remove the 'target' property instead of 'errors' in the strategy, I get JSON that looks like:  

{"op1":{"label":"test1","number1":95.1,"number2":84.1},"op2":{"label":"test2","number1":85.1,"number2":74.1},"errors":{"autoGrowNestedPaths":true,"autoGrowCollectionLimit":2147483647,"objectName":"gsongrailstest.Operations","messageCodesResolver":{"prefix":""},"errors":[],"suppressedFields":[],"nestedPath":"","nestedPathStack":[]}}

I've italicized the 'extra information that Grails seems to be including.
Reply all
Reply to author
Forward
0 new messages