package com.alibaba.fastjson.support.spring;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializeFilter;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.util.IOUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.servlet.view.AbstractView;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Fastjson for Spring MVC View.
*
* @author libinsong1204@gmail.com
* @author VictorZeng
* @see AbstractView
* @since 1.2.9
*/
public class FastJsonJsonView extends AbstractView {
/**
* default content type
*/
public static final String DEFAULT_CONTENT_TYPE = "application/json;charset=UTF-8";
@Deprecated
protected Charset charset = Charset.forName("UTF-8");
@Deprecated
protected SerializerFeature[] features = new SerializerFeature[0];
@Deprecated
protected SerializeFilter[] filters = new SerializeFilter[0];
@Deprecated
protected String dateFormat;
/**
* renderedAttributes
*/
private Set<String> renderedAttributes;
/**
* disableCaching
*/
private boolean disableCaching = true;
/**
* updateContentLength
*/
private boolean updateContentLength = true;
/**
* extractValueFromSingleKeyModel
*/
private boolean extractValueFromSingleKeyModel = false;
/**
* with fastJson config
*/
private FastJsonConfig fastJsonConfig = new FastJsonConfig();
/**
* Set default param.
*/
public FastJsonJsonView() {
setContentType(DEFAULT_CONTENT_TYPE);
setExposePathVariables(false);
}
/**
* @return the fastJsonConfig.
* @since 1.2.11
*/
public FastJsonConfig getFastJsonConfig() {
return fastJsonConfig;
}
/**
* @param fastJsonConfig the fastJsonConfig to set.
* @since 1.2.11
*/
public void setFastJsonConfig(FastJsonConfig fastJsonConfig) {
this.fastJsonConfig = fastJsonConfig;
}
@Deprecated
public void setSerializerFeature(SerializerFeature... features) {
this.fastJsonConfig.setSerializerFeatures(features);
}
@Deprecated
public Charset getCharset() {
return this.fastJsonConfig.getCharset();
}
@Deprecated
public void setCharset(Charset charset) {
this.fastJsonConfig.setCharset(charset);
}
@Deprecated
public String getDateFormat() {
return this.fastJsonConfig.getDateFormat();
}
@Deprecated
public void setDateFormat(String dateFormat) {
this.fastJsonConfig.setDateFormat(dateFormat);
}
@Deprecated
public SerializerFeature[] getFeatures() {
return this.fastJsonConfig.getSerializerFeatures();
}
@Deprecated
public void setFeatures(SerializerFeature... features) {
this.fastJsonConfig.setSerializerFeatures(features);
}
@Deprecated
public SerializeFilter[] getFilters() {
return this.fastJsonConfig.getSerializeFilters();
}
@Deprecated
public void setFilters(SerializeFilter... filters) {
this.fastJsonConfig.setSerializeFilters(filters);
}
/**
* Set renderedAttributes.
*
* @param renderedAttributes renderedAttributes
*/
public void setRenderedAttributes(Set<String> renderedAttributes) {
this.renderedAttributes = renderedAttributes;
}
/**
* Check extractValueFromSingleKeyModel.
*
* @return extractValueFromSingleKeyModel
*/
public boolean isExtractValueFromSingleKeyModel() {
return extractValueFromSingleKeyModel;
}
/**
* Set extractValueFromSingleKeyModel.
*
* @param extractValueFromSingleKeyModel
*/
public void setExtractValueFromSingleKeyModel(
boolean extractValueFromSingleKeyModel) {
this.extractValueFromSingleKeyModel = extractValueFromSingleKeyModel;
}
@Override
protected void renderMergedOutputModel(Map<String, Object> model, //
HttpServletRequest request, //
HttpServletResponse response) throws Exception {
Object value = filterModel(model);
ByteArrayOutputStream outnew = new ByteArrayOutputStream();
int len = JSON.writeJSONString(outnew, //
fastJsonConfig.getCharset(), //
value, //
fastJsonConfig.getSerializeConfig(), //
fastJsonConfig.getSerializeFilters(), //
fastJsonConfig.getDateFormat(), //
JSON.DEFAULT_GENERATE_FEATURE, //
fastJsonConfig.getSerializerFeatures());
if (this.updateContentLength) {
// Write content length (determined via byte array).
response.setContentLength(len);
}
// Flush byte array to servlet output stream.
ServletOutputStream out = response.getOutputStream();
outnew.writeTo(out);
outnew.close();
out.flush();
}
@Override
protected void prepareResponse(HttpServletRequest request, //
HttpServletResponse response) {
setResponseContentType(request, response);
response.setCharacterEncoding(fastJsonConfig.getCharset().name());
if (this.disableCaching) {
response.addHeader("Pragma", "no-cache");
response.addHeader("Cache-Control", "no-cache, no-store, max-age=0");
response.addDateHeader("Expires", 1L);
}
}
/**
* Disables caching of the generated JSON.
* <p>
* Default is {@code true}, which will prevent the client from caching the
* generated JSON.
*/
public void setDisableCaching(boolean disableCaching) {
this.disableCaching = disableCaching;
}
/**
* Whether to update the 'Content-Length' header of the response. When set
* to {@code true}, the response is buffered in order to determine the
* content length and set the 'Content-Length' header of the response.
* <p>
* The default setting is {@code false}.
*/
public void setUpdateContentLength(boolean updateContentLength) {
this.updateContentLength = updateContentLength;
}
/**
* Filters out undesired attributes from the given model. The return value
* can be either another {@link Map}, or a single value object.
* <p>
* Default implementation removes {@link BindingResult} instances and
* entries not included in the {@link #setRenderedAttributes(Set)
* renderedAttributes} property.
*
* @param model the model, as passed on to {@link #renderMergedOutputModel}
* @return the object to be rendered
*/
protected Object filterModel(Map<String, Object> model) {
Map<String, Object> result = new HashMap<String, Object>(model.size());
Set<String> renderedAttributes = !CollectionUtils.isEmpty(this.renderedAttributes) ? //
this.renderedAttributes //
: model.keySet();
for (Map.Entry<String, Object> entry : model.entrySet()) {
if (!(entry.getValue() instanceof BindingResult)
&& renderedAttributes.contains(entry.getKey())) {
result.put(entry.getKey(), entry.getValue());
}
}
if (extractValueFromSingleKeyModel) {
if (result.size() == 1) {
for (Map.Entry<String, Object> entry : result.entrySet()) {
return entry.getValue();
}
}
}
return result;
}
}