Filtering JSON feeds from Spring 3's REST support using addMixInAnnotations

本文介绍如何在Spring3框架中利用Jackson API进行JSON序列化时的字段过滤,通过定义接口作为过滤器并使用@JsonFilter注解,可以灵活控制不同请求返回的数据字段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring3 allows you to output JSON payloads quite easily with its REST support. It uses Jackson (an advanced, fast API that does JSON serialization essentially converts Java object to JSON).

The problem is that depending on which REST request we get, we may want to show different parts of a particular object.

We don't want 10 different subclasses of the same object. Jackson provides a way to filter objects based on addMixInAnnotations (read previous post for more detail).

We would like to easily vary what gets returned on a request method handler by request handler basis.


A request handler in Spring3 that returns a JSON payload looks like this:

@RequestMapping(value = "/{locale}/apps/{publicSlug}/", method = RequestMethod.GET)
public @ResponseBody ApplicationEntry getAppDetail(@PathVariable String  publicSlug, @PathVariable String locale, 
HttpServletResponse response, HttpServletRequest request) throws Exception {
                 ...
return ApplicationEntry.findApplicationEntry( publicSlug, country, language);
}


We would like to do this to apply a JSON filter.

Define an interface (which is the filter) as follows:

@JsonIgnoreProperties( { "country",
})
@JsonPropertyOrder(value={"title", "id", "version",  "price", "summary"})
public interface AppDetailFilter {

}

Then use the annotation to filter the response as follows:


@JsonFilter(mixin=AppDetailFilter.class)
@RequestMapping(value = "/{locale}/apps/{ publicSlug}/", method = RequestMethod.GET)
public @ResponseBody ApplicationEntry getAppDetail(@PathVariable String  publicSlug, @PathVariable String locale, 
HttpServletResponse response, HttpServletRequest request) throws Exception {
                 ...
return ApplicationEntry.findApplicationEntry( publicSlug, country, language);
}



Ok so we added this capability.

This is what the annotation looks like:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;


@Retention(RetentionPolicy.RUNTIME)
public @interface JsonFilter {
Class<?> mixin();
}


Then we just write the glue code to apply the behavior of outputting the returned object to the HTTP stream as follows:

public aspect JsonFilterAspect {

protected final Logger log = Logger.getLogger(getClass());

pointcut filterJsonResponses(): execution(@JsonFilter public * com.somecompany.someapp.web..*.*(..));

Object around() : filterJsonResponses() {
MethodSignature msig = (MethodSignature) thisJoinPoint.getSignature();
                /* Grab the annotation. */
JsonFilter annotation = msig.getMethod().getAnnotation(JsonFilter.class);
Class<?> mixin = annotation.mixin();
ObjectMapper mapper = new ObjectMapper();
mapper.getSerializationConfig().addMixInAnnotations(msig.getMethod().getReturnType(), mixin);

try {
mapper.writeValue(WebContext.getInstance().getResponse().getOutputStream(), proceed());
} catch(Exception ex) {
throw new RuntimeException(ex);
}
return null;
}
}


The above applies custom JSON serialization code based on Jackson's support for adding mixin annotations (described in the previous post).


You might wonder what WebContext is. It is a helper class I wrote that gets populated by a ServletFilter. The context is stored in a ThreadLocal and then removed after the request is done (both by the filter). For completeness, here is the filter and WebContext object.

@Component("webContextFilter")
public class WebContextFilter extends GenericFilterBean {

@Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
ServletContext servletContext = this.getServletContext();
WebContext.create(request, response, servletContext);
chain.doFilter(request, response);
WebContext.clear();
}

}


@RooJavaBean
@RooToString
public class WebContext {

private static ThreadLocal<WebContext> tlv = new ThreadLocal<WebContext>();
private HttpServletRequest request;
private HttpServletResponse response;
private ServletContext servletContext;
protected WebContext() {}
private WebContext(HttpServletRequest request, HttpServletResponse response,
ServletContext servletContext) {
this.request = request;
this.response = response;
this.servletContext = servletContext;
}
public static WebContext getInstance() {
return tlv.get();
}
public static void create(HttpServletRequest request, HttpServletResponse response,
ServletContext servletContext) {
WebContext wc = new WebContext(request, response, servletContext);
tlv.set(wc);
}
public static void clear() {
tlv.set(null);
}
}

 

Now we can annotate any request handler with  @JsonFilter(mixin=AppDetailFilter.class) and it will apply the Jackson JSON custom streaming, i.e., filtering.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值