java - Streaming JSON directly to response with Jackson -


currently, need send large json object ajax request. purpose using following controller method works fine.

   @requestmapping(method = requestmethod.post,params = {"dynamicscenario"})    @responsebody     public string getdynamicscenariodata(@requestparam map<string, string> map) throws jsonparseexception, jsonmappingexception, ioexception  {    objectmapper mapper = new objectmapper();      @suppresswarnings("unchecked")     map<string,object> queryparameters = mapper.readvalue(map.get("parameters") , map.class);      map<string, object> getdata = service.rundynamicscenario(queryparameters, map.get("querystring"));      return writer.writevalueasstring(getdata); //here java throws java.lang.outofmemoryerror: java heap space memory }  

update: ajax is:

          $.ajax({           type: "post",           url: "dynamicscenario.htm",           data : tags,           datatype: "json",           success: function(data){}); 

my dispatcherservlet settings:

           public class applicationinitializer implements      webapplicationinitializer       {        public void onstartup(servletcontext servletcontext) throws            servletexception       {     annotationconfigwebapplicationcontext context = new annotationconfigwebapplicationcontext();     context.register(applicationconfig.class);      servletcontext.addlistener(new contextloaderlistener(context));      servletregistration.dynamic servletregistration = servletcontext.addservlet("dispatcher", new dispatcherservlet(context));     servletregistration.setloadonstartup(1);     servletregistration.addmapping("*.htmlx");   } } 

i using jackson serialize map of different objects , send ajax. however, if size of json large java throws out of memory. know jackson method writer.writevalueasstring inefficient because writing string there other alternative? can't use plain java pojo because don't know objects map have serialize contain can't map java object. ideas? thanks

the issue want solve is

return writer.writevalueasstring(getdata);  

creating big of string , causing outofmemoryerror. jackson supports streaming api, spring makes use of in mappingjackson2httpmessageconverter, handles serializing pojos json in response body (and request body).

there few ways handle this. easiest change return type map<string, object , return corresponding object directly.

@requestmapping(method = requestmethod.post,params = {"dynamicscenario"}) @responsebody public map<string, object> getdynamicscenariodata(@requestparam map<string, string> map) throws jsonparseexception, jsonmappingexception, ioexception  {      objectmapper mapper = new objectmapper();       @suppresswarnings("unchecked")      map<string,object> queryparameters = mapper.readvalue(map.get("parameters") , map.class);       map<string, object> getdata = service.rundynamicscenario(queryparameters, map.get("querystring"));       return getdata; }  

spring take care of serializing getdata streaming results directly response outputstream.

this won't work on own. one, url using access service

/dynamicscenario.htm 

is causing spring's content negotiation kick in. sees .htm , thinks expecting text/html content. either turn off content negotiation or use url without extension

/dynamicscenario 

spring then, instead, @ accept headers figuring out client expecting. since

datatype: "json" 

it's going write application/json mappingjackson2httpmessageconverter.


here more things fix

annotationconfigwebapplicationcontext context = new annotationconfigwebapplicationcontext(); context.register(applicationconfig.class);  servletcontext.addlistener(new contextloaderlistener(context));  servletregistration.dynamic servletregistration = servletcontext.addservlet("dispatcher", new dispatcherservlet(context)); servletregistration.setloadonstartup(1); servletregistration.addmapping("*.htmlx"); 

you're making both contextloaderlistener , dispatcherservlet load same applicationcontext. unnecessary , potentially harmful. dispatcherservlet uses contextloaderlistener context parent context. can read more here:

if applicationconfig contains mvc configuration elements, have dispatcherservlet load it. won't need contextloaderlistener.

if has other types of config elements unrelated mvc stack, split 2 @configuration classes. pass mvc 1 dispatcherservlet , other contextloaderlistener.

don't pass json form parameters. pass directly request body , use @requestbody deserialize directly expected type.

@requestmapping(method = requestmethod.post) @responsebody public map<string, object> getdynamicscenariodata(@requestbody map<string,object> queryparameters) throws jsonparseexception, jsonmappingexception, ioexception  {      map<string, object> getdata = service.rundynamicscenario(queryparameters, /* find better way pass map.get("querystring") */);       return getdata; } 

you save objectmapper object on each request (it's heavy object) , let spring take care of deserializing (again streaming). you'll have find way pass querystring (you can still pass single query parameter , `@requestparam).


Comments