问题
getting a stack overflow when deep rendering a domain object with json views.
I have created a Customer domain object and Sites domain object, where Customer hasMany sites.
i have created two customers and one site in bootsrap and assigned the site to first customer.
I have used gorm data service to create a customerService instance from interface.
As per json views documentation (section 2.5.2) i have assured that when i use the index action on the controller that i do a join fetch for sites and ensure i have the customer list and the sites in one hibernate session to pass to the view.
in the _customerRest.gson view in call 'json g.render (customer , [deep:true])'. I have a _siteRest.gson template in place for the Site Domain object/controller pairing ( this works - when i just use rest to get sites, or specific site this renders correctly)
when i invoke my '/api/customers' uri with Postman, i get a repeated json output like this
[{"id":1,"sites":[{"id":1,"name":"Canary Wharf","customer":{"id":1,"sites":[{"id":1,"name":"Canary Wharf","customer":{"id":1,"sites":[{"id":1,"name":"Canary Wharf","customer":{"id":1,"sites":[{"id":1,"name":"Canary Wharf","customer":{"id":1,"sites":[{"id":1,"name":"Canary Wharf","customer":{"id":1,"sites":[{"id":1,"name":"Canary Wharf","customer":{"id":1,"sites":[{"id":1,"name":"Canary Wharf","customer":{"id":1,"sites":[{"id":1,"name":"Canary Wharf","customer":{"id":1,"sites":[{"id":1,"name":"Canary Wharf","customer":{"id":1,"sites":[{"id":1,"name":"Canary Wharf","customer":{"id":1,"sites":[{"id":1,"name":"Canary Wharf","customer":{"id":1,"sites":[{"id":1,"name":"Canary Wharf","customer":{"id":1...
and then a stackoverflow like this
Grails application running at http://localhost:8080 in environment: development
CustomerRest.customerRest.index method invoked
param map to customerService contains [controller:customerRest, action:index, max:10, fetch:[sites:join]]
service returned collection [ttrestapi.model.Customer : 1, ttrestapi.model.Customer : 2]
2018-03-20 16:36:21.504 ERROR --- [nio-8080-exec-4] o.g.web.errors.GrailsExceptionResolver : StackOverflowError occurred when processing request: [GET] /api/customers
Stacktrace follows:
java.lang.reflect.InvocationTargetException: null
at org.grails.core.DefaultGrailsControllerClass$ReflectionInvoker.invoke(DefaultGrailsControllerClass.java:211)
at org.grails.core.DefaultGrailsControllerClass.invoke(DefaultGrailsControllerClass.java:188)
at org.grails.web.mapping.mvc.UrlMappingsInfoHandlerAdapter.handle(UrlMappingsInfoHandlerAdapter.groovy:90)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
at org.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:77)
at org.grails.web.filters.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:67)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: grails.views.ViewRenderException: Error rendering view: Error rendering view: null
at grails.views.AbstractWritableScript.writeTo(AbstractWritableScript.groovy:43)
at grails.views.mvc.GenericGroovyTemplateView.renderMergedOutputModel(GenericGroovyTemplateView.groovy:73)
at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303)
at grails.views.mvc.renderer.DefaultViewRenderer.render(DefaultViewRenderer.groovy:111)
at grails.artefact.controller.RestResponder$Trait$Helper.internalRespond(RestResponder.groovy:188)
at grails.artefact.controller.RestResponder$Trait$Helper.respond(RestResponder.groovy:98)
at ttrestapi.model.CustomerRestController.index(CustomerRestController.groovy:27)
... 14 common frames omitted
Caused by: grails.views.ViewRenderException: Error rendering view: null
at grails.views.AbstractWritableScript.writeTo(AbstractWritableScript.groovy:43)
at grails.plugin.json.view.api.internal.DefaultGrailsJsonViewHelper$6.writeTo(DefaultGrailsJsonViewHelper.groovy:792)
at grails.plugin.json.view.JsonViewWritableScript.json(JsonViewWritableScript.groovy:123)
at grails.plugin.json.view.JsonViewWritableScript.json(JsonViewWritableScript.groovy:146)
at ttRestApi_customerRest_index_gson.run(ttRestApi_customerRest_index_gson:9)
at grails.plugin.json.view.JsonViewWritableScript.doWrite(JsonViewWritableScript.groovy:28)
at grails.views.AbstractWritableScript.writeTo(AbstractWritableScript.groovy:40)
... 20 common frames omitted
Caused by: java.lang.StackOverflowError: null
at java.io.InputStream.<init>(InputStream.java:45)
at java.util.zip.ZipFile$ZipFileInputStream.<init>(ZipFile.java:711)
at java.util.zip.ZipFile.getInputStream(ZipFile.java:375)
at java.util.jar.JarFile.getInputStream(JarFile.java:447)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:454)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at grails.plugin.json.builder.StreamingJsonBuilder$StreamingJsonDelegate.curryDelegateAndGetContent(StreamingJsonBuilder.java:809)
at grails.plugin.json.builder.StreamingJsonBuilder$StreamingJsonDelegate.writeObject(StreamingJsonBuilder.java:775)
at grails.plugin.json.builder.StreamingJsonBuilder$StreamingJsonDelegate.writeCollectionWithClosure(StreamingJsonBuilder.java:766)
at grails.plugin.json.builder.StreamingJsonBuilder$StreamingJsonDelegate.writeObjects(StreamingJsonBuilder.java:706)
at grails.plugin.json.builder.StreamingJsonBuilder$StreamingJsonDelegate.call(StreamingJsonBuilder.java:610)
at .....
if i remove the [deep:true] option my json will render but without the showing the site details in the output - just the site id as child.
latest revision of this sample project is here on github : https://github.com/woodmawa/ttRestApi
has any one else had this problem is this an error in my template gson somewhere ?
PS. if a change the render command to json g.render (customer , [expand:['sites']]) - that seems to work correctly as you'd expect. but the [deep:true] option fails as shown above.
my index action in the CustomerRestController looks like this with debug statements in it
def index(Integer max) {
println "CustomerRest.customerRest.index method invoked"
params.max = Math.min(max ?: 10, 100)
params.put("fetch", [sites:"join"]) //force join fetch on sites
println "param map to customerService contains $params"
Collection result = customerService.list(params)
println "service returned collection $result"
// respond customerService.list(params), [ttrestapi.model:[customerCount: customerService.count()], view:"fred"]
respond result, [model:[customerCount: customerService.count()]]
}
when i look at the result from the customerService.list query i have the first customer and his single site through the fetch:[sites:"join"] query as expected
my _customerRest.gson looks like
import ttrestapi.model.Customer
model {
Customer customer
}
json g.render (customer , [deep:true])
and corresponding sites _siteRest.gson looks like this
import ttrestapi.model.Site
model {
Site site
}
json jsonapi.render (site)
回答1:
I have filed a bug report at https://github.com/grails/grails-views/issues/165.
The template at https://github.com/jeffbrown/williamwoodman/blob/86b0bed52fd00bb09cee22e49234797ef44f3a35/grails-app/views/customer/_customer.gson looks like this:
import williamwoodman.Customer
model {
Customer customer
}
json g.render(customer, [deep: true])
That will trigger the bug. Depending on what you want your rendered JSON to look like, you have a number of options. One thing you could do is author your template to look something like this:
import williamwoodman.Customer
model {
Customer customer
}
json {
id customer.id
name customer.name
sites g.render(customer.sites ?: [])
}
In that sample project, changing the template to look like that and then sending a request to http://localhost:8080/customer will yield JSON which looks like this:
[{"id":1,"name":"Customer One",
"sites":[{"id":1,"name":"Site One","customer":{"id":1}},
{"id":3,"name":"Site Three","customer":{"id":1}},
{"id":2,"name":"Site Two","customer":{"id":1}}]}]
You might want to exclude the customer
from the individual site
maps, or you might not.
I hope that helps.
来源:https://stackoverflow.com/questions/49389879/grails-v3-3-3-json-views-1-2-7-getting-stack-overflow-when-doing-a-deep-rende