直接流式传输JSON以响应Jackson

当前,我需要向ajax请求发送一个大型json对象。为此,我使用了下面的工作正常的控制器方法。

   @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

}

更新:我的ajax是:

          $.ajax({

type: "POST",

url: "dynamicScenario.htm",

data : tags,

dataType: "json",

success: function(data){});

我的DispatcherServlet设置:

           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");

}

}

我正在使用Jackson序列化不同对象的地图,然后将其发送回ajax。但是,如果json的大小很大,则Java会抛出内存不足。我知道Jackson方法writer.writeValueAsString效率低下,因为它写入字符串,但是还有其他选择吗?我不能使用普通的Java

POJO,因为我不知道要序列化的映射将包含哪些对象,因此我不能简单地将其映射到某个Java对象。有任何想法吗?谢谢

回答:

您要解决的问题是

return writer.writeValueAsString(getData);

造成太大的String并导致了OutOfMemoryError。Jackson支持Streaming

API(Spring在其中使用),该APIMappingJackson2HttpMessageConverter在响应正文(和请求正文)中处理将POJO序列化为JSON。

有几种方法可以解决此问题。最简单的方法是将您的返回类型更改为,Map<String, Object然后直接返回相应的对象。

@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将getData通过将结果直接流式传输到响应中来进行序列化OutputStream

这将无法单独工作。例如,您用来访问服务的网址

/dynamicScenario.htm

导致Spring的内容协商开始。它看到.htm并认为您正在期待text/html内容。关闭内容协商或使用不带扩展名的URL

/dynamicScenario

然后,Spring将查看Accept标头,以弄清客户的期望。因为那是

dataType: "json"

它将application/json与一起写入MappingJackson2HttpMessageConverter


这里还有一些要解决的问题

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");

您目前正在ContextLoaderListenerDispatcherServlet加载相同ApplicationContext。这是不必要的,并且可能有害。在DispatcherServlet已经使用的ContextLoaderListener上下文父上下文。

如果ApplicationConfig仅包含MVC配置元素,则仅DispatcherServlet加载它。您不需要ContextLoaderListener

如果它具有与MVC堆栈无关的其他类型的配置元素,请将其分为两个@Configuration类。将MVC一个传递给DispatcherServlet,另一个传递给ContextLoaderListener

不要将JSON作为表单参数传递。将其直接传递到请求正文中,并用于@RequestBody将其反序列化为所需的类型。

@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 a better way to pass this map.get("queryString") */);

return getData;

}

ObjectMapper在每个请求上都为自己节省了一个对象(这是一个沉重的对象),并让Spring负责所有反序列化(再次进行流传输)。您必须找到另一种传递的方法queryString(您仍然可以将其作为单个查询参数传递,并通过`@RequestParam获取)。

以上是 直接流式传输JSON以响应Jackson 的全部内容, 来源链接: utcz.com/qa/418636.html

回到顶部