SpringMVC返回JSON方案

  SpringMVC已經大行其道。通常的,都是返回JSP視圖。若是須要返回JSON格式,咱們大都掌握了一些方法。html

  在ContentNegotiatingViewResolver以前,通常使用XmlViewResolver的location屬性,手工編寫一個視圖專門處理Json類型,就是將返回的數據封裝成Json輸出。下面介紹該方案具體代碼。java

  exam-servlet.xml:web

    <bean id="xmlViewResolver" class="org.springframework.web.servlet.view.XmlViewResolver">
        <property name="order" value="1"/>
        <property name="location" value="/WEB-INF/views.xml"/>
    </bean>
    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="order" value="2"></property>
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
        <property name="viewClass"
            value="org.springframework.web.servlet.view.JstlView" />
    </bean>

  views.xmlspring

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="json" class="cc.monggo.web.views.JsonView"></bean>
</beans>

  JsonView.java編程

/**
 * 
 */
package cc.monggo.web.views;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.json.JSON;
import net.sf.json.JSONArray;
import net.sf.json.JSONSerializer;
import net.sf.json.JsonConfig;
import net.sf.json.filters.OrPropertyFilter;
import net.sf.json.util.PropertyFilter;

import org.springframework.web.servlet.view.AbstractView;

import cc.monggo.web.form.BaseForm;

/**
 * @author fangjinsong
 * 
 */
public class JsonView extends AbstractView {
    private static final String DEFAULT_JSON_CONTENT_TYPE = "application/json;charset=UTF-8";
    private boolean forceTopLevelArray = false;
    private boolean skipBindingResult = true;
    private JsonConfig jsonConfig = new JsonConfig();

    public JsonView() {
        super();
        setContentType(DEFAULT_JSON_CONTENT_TYPE);
    }

    public boolean isForceTopLevelArray() {
        return forceTopLevelArray;
    }

    public void setForceTopLevelArray(boolean forceTopLevelArray) {
        this.forceTopLevelArray = forceTopLevelArray;
    }

    public boolean isSkipBindingResult() {
        return skipBindingResult;
    }

    public void setSkipBindingResult(boolean skipBindingResult) {
        this.skipBindingResult = skipBindingResult;
    }

    public JsonConfig getJsonConfig() {
        return jsonConfig;
    }

    public void setJsonConfig(JsonConfig jsonConfig) {
        this.jsonConfig = jsonConfig != null ? jsonConfig : new JsonConfig();
        if (skipBindingResult) {
            PropertyFilter jsonPropertyFilter = this.jsonConfig.getJavaPropertyFilter();
            if (jsonPropertyFilter == null) {
                this.jsonConfig.setJsonPropertyFilter(new BindingResultPropertyFilter());
            } else {
                this.jsonConfig.setJsonPropertyFilter(new OrPropertyFilter(new BindingResultPropertyFilter(), jsonPropertyFilter));
            }
        }
    }

    public static String getDefaultJsonContentType() {
        return DEFAULT_JSON_CONTENT_TYPE;
    }

    public boolean isIgnoreDefaultExcludes() {
        return getJsonConfig().isIgnoreDefaultExcludes();
    }

    public void setExcludedProperties(String[] excludedProperties) {
        jsonConfig.setExcludes(excludedProperties);
    }

    public void setIgnoreDefaultExcludes(boolean ignoreDefaultExcludes) {
        jsonConfig.setIgnoreDefaultExcludes(ignoreDefaultExcludes);
    }

    protected String[] getExcludedProperties() {
        return jsonConfig.getExcludes();
    }

    @Override
    protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        response.setContentType(getContentType());
        Map newModel = new HashMap();
        for (Iterator it = model.keySet().iterator(); it.hasNext();) {
            Object key = it.next();
            Object value = model.get(key);
            if (!(value instanceof BaseForm)) {
                newModel.put(key, value);
            }
        }
        writeJSON(newModel, request, response);
    }

    protected void writeJSON(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        JSON json = createJSON(model, request, response);
        if (forceTopLevelArray) {
            json = new JSONArray().element(json);
        }
        json.write(response.getWriter());
    }

    protected final JSON defaultCreateJSON(Map model) {
        if (skipBindingResult && jsonConfig.getJavaPropertyFilter() == null) {
            jsonConfig.setJsonPropertyFilter(new BindingResultPropertyFilter());
        }
        return JSONSerializer.toJSON(model, jsonConfig);
    }

    private JSON createJSON(Map model, HttpServletRequest request, HttpServletResponse response) {
        return defaultCreateJSON(model);
    }

    private static class BindingResultPropertyFilter implements PropertyFilter {
        @Override
        public boolean apply(Object source, String name, Object value) {
            return name.startsWith("org.springframework.validation.BindingResult");
        }
    }

}
View Code

  這種方案會增長一個views.xml文件。另外一個方案使用ContentNegotiatingViewResolver內容協商解析器。該解析器能夠解析多種Controller返回結果,方法是分派給其餘的解析器解析。這樣就能夠返回JSP視圖,Xml視圖,Json視圖。其中Json視圖由MappingJacksonJsonView視圖表示。ContentNegotiatingViewResolver的通常配置以下:json

    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="order" value="1" />  
        <property name="ignoreAcceptHeader" value="true" />
        <property name="favorParameter" value="false" /> 
        <property name="defaultContentType" value="text/html" />
        <property name="mediaTypes">
            <map>
                <entry key="json" value="application/json" />
            </map>
        </property>
        <property name="viewResolvers">
            <list>
                <bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
                <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
                    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
                    <property name="prefix" value="/WEB-INF/jsp/" />
                    <property name="suffix" value=".jsp"></property>
                </bean>
            </list>
        </property>
        <property name="defaultViews">
            <list>
                <bean id="json" class="com.mogee.web.views.MogeeMappingJacksonJsonView" />
            </list>
        </property>
    </bean>
View Code

  要返回Json格式數據,Controller中通常使用JsonResult,@responseBody等技術。例如:app

@RequestMapping("/json3.json")
    public JsonResult testJson3(@RequestBody User u){
        log.info("handle json output from ContentNegotiatingViewResolver");
        return new JsonResult(true,"return ok");
    }
View Code

  這樣一來,有一個問題:Controller老是返回Object對象,不能返回ModelAndview類型。按筆者的經驗,常常有這樣的需求:一個請求,若是返回了正確的結果,返回正常的JSP;若是返回了錯誤的結果,咱們但願返回Json類型數據。爲了知足這樣要求,通常使用response.getWriter.print("")技術。筆者並不喜歡這樣寫法,不像Spring的風格。因此下面筆者將提出第二種方案。二者都能兼顧。Contoller代碼以下。jsp

@RequestMapping(value = "/test", method = RequestMethod.GET)
    public ModelAndView jsonView(HttpServletRequest request, HttpServletResponse response, HelloForm form)
            throws Exception {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("hello", "Hello");
        ModelAndView mv = new ModelAndView(new MappingJacksonJsonView(), map);
        return mv;
    }

  經過這樣的代碼,能夠在Controller中靈活控制,返回JSON或者JSP均可以。看似完美的方案,也有一個問題,返回的時候會將Form也打包到Json中去。以下:ide

  緣由是由於MappingJacksonJsonView代碼中將數據map,mv.add(),還有form,還有錯誤提示等四項數據都加入Json,原程序只是過濾了錯誤提示,因此就留下了form數據。this

爲此咱們須要重寫MappingJacksonJsonView,將剩餘三種數據中的form也過濾掉。通常的,form都會繼承BaseForm,因此只要使用instanceof判斷便可。這樣的改造簡潔明瞭,符合Spring編程的特色。具體代碼以下:

public class MogeeMappingJacksonJsonView extends MappingJacksonJsonView {
    @Override
    protected Object filterModel(Map<String, Object> model) {
        Map<String, Object> result = new HashMap<String, Object>(model.size());
        Set<String> renderedAttributes = !CollectionUtils.isEmpty(super.getRenderedAttributes()) ? super
                .getRenderedAttributes() : model.keySet();
        for (Map.Entry<String, Object> entry : model.entrySet()) {
            if (!(entry.getValue() instanceof BindingResult) && renderedAttributes.contains(entry.getKey())) {
                if (!(entry.getValue() instanceof BaseForm)) {
                    result.put(entry.getKey(), entry.getValue());
                }
            }
        }
        return result;
    }
}

相應的,exam-servlet.xml中也要修改,代碼以下:

如此,就完成了輸出Json的方案。

                                                       方勁鬆 東莞虎門信豐物流

                                                        2015.3.20

相關文章
相關標籤/搜索