簡體   English   中英

直接流式傳輸JSON以響應Jackson

[英]Streaming JSON directly to response with 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");
  }
}

我正在使用傑克遜序列化不同對象的地圖,然后將其發送回ajax。 但是,如果json的大小很大,則Java會拋出內存不足。 我知道Jackson方法writer.writeValueAsString效率低下,因為它寫入字符串,但是還有其他選擇嗎? 我不能使用普通的Java POJO,因為我不知道要序列化的映射將包含哪些對象,因此我不能簡單地將其映射到某些Java對象。 有任何想法嗎? 謝謝

您要解決的問題是

return writer.writeValueAsString(getData); 

創建太大的String並導致OutOfMemoryError Jackson支持Streaming API ,Spring在其MappingJackson2HttpMessageConverter使用了該API ,該API在響應正文(和請求正文)中處理將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將通過將結果直接流式傳輸到響應OutputStream來對getData進行序列化。

這將無法單獨工作。 例如,您用來訪問服務的網址

/dynamicScenario.htm

導致Spring的內容協商開始。它看到.htm並認為您期望text/html內容。 關閉內容協商或使用不帶擴展名的URL

/dynamicScenario

然后,Spring將查看Accept標頭,以弄清客戶的期望。 因為那是

dataType: "json"

它將使用MappingJackson2HttpMessageConverter編寫application/json


這里還有一些要解決的問題

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獲取)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM