从JSON有效负载中获取特定属性作为MVC方法参数

时间:2016-06-20 13:14:50

标签: java json spring spring-mvc

我之前问了一个类似的问题:this one

现在我有一个类似但不同的问题。

My Spring MVC控制器模型是一个JSON有效负载,带有一组已定义的属性,遗憾的是,这些属性不属于我项目中的类。

E.g。

{
    "userId" : "john",
    "role" : "admin"
}

我需要将userIdrole视为单独的String

我目前有两种方法来声明控制器方法

public ResponseObject mvc(@RequestBody MyCustomDTO dto){

    String userId = dto.getUserId();
    String role = dto.getRole();
}

public ResponseObject mvc(@RequestBody ModelMap map){
    String userId = (String)map.get("userId");
    String role = (String)map.get("role");
}

我被要求找到一个不同的实现,因为1)需要为每个参数组合创建一个自定义DTO类(大多数情况需要1个命名参数,例如delete(productId))和2)涉及一个实体没有严格定义。特别是在处理列表时,它可以包含需要在运行时检查的任意值。

我发现Spring MVC不支持从JSON请求体中解析@ModelAttribute。我做错了什么,或者只是Spring没有做到这一点?我可以从Request Body到方法参数中获取特定属性,无论是简单的原语还是整个POJO?

在第二种情况下,最好向Spring开发人员请求一个有用的功能。

Spring版本是4.2.x. 这个问题与之前的链接有关但不同之处在于,现在我将单个属性封装到Javascript对象中,因此Jackson需要反序列化的对象不会是原始的,而是POJO。

2 个答案:

答案 0 :(得分:2)

您无法轻松获得个人成员,因为Spring MVC没有任何内置工具可以做到这一点。一种选择是编写自己的注释,该注释描述了例外JSON对象体的根处的参数。然后编写并注册一个新的HandlerMethodArgumentResolver实现,该实现在处理程序方法参数上处理该注释。

这不是一项简单的任务。由于您无法多次使用请求内容,因此您必须以某种方式将其保存在Filter中。现在,让我们忽略这个限制,并假设我们只需要一个参数。您可以定义注释

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@interface JsonObjectProperty {
    String name();
}

HandlerMethodArgumentResolver

class JsonObjectPropertyResolver implements HandlerMethodArgumentResolver {
    /**
     * Configured as appropriate for the JSON you expect.
     */
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(JsonObjectProperty.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
            WebDataBinderFactory binderFactory) throws Exception {
        Class<?> parameterType = parameter.getParameterType();

        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
        MediaType contentType = inputMessage.getHeaders().getContentType();
        if (!contentType.equals(MediaType.APPLICATION_JSON_UTF8)) {
            throw new HttpMessageNotReadableException(
                    "Could not read document. Expected Content-Type " + MediaType.APPLICATION_JSON_UTF8 + ", was " + contentType + ".");
        }
        // handle potential exceptions from this as well
        ObjectNode rootObject = objectMapper.readValue(inputMessage.getBody(), ObjectNode.class);
        if (parameterType == String.class) {
            JsonObjectProperty annotation = parameter.getParameterAnnotation(JsonObjectProperty.class);
            return rootObject.get(annotation.name()).asText();
        }
        // handle more
        throw new HttpMessageNotReadableException("Could not read document. Parameter type " + parameterType + " not parseable.");
    }
}

最后是处理程序方法

@RequestMapping(value = "/json-new", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public String handleJsonProperty(@JsonObjectProperty(name = "userId") String userId) {
    String result = userId;
    System.out.println(result);
    return result;
}

您必须正确注册JsonObjectPropertyResolver。完成后,它将能够直接将该JSON属性提取到参数中。

答案 1 :(得分:0)

您可以使用一些JSON内联解析器(类似于XML Xpath),您可以在其中提供JSON字符串并要求解析器将一些子文档检索为String,List或Map。其中一个例子是OGNL。它是一个非常强大的工具,虽然它不是唯一一个而且效率最高,但仍然是成熟稳定的Apache产品。因此,在您的情况下,您可以将JSON字符串提供给OGNL并告诉它检索属性&#34; userId&#34;和#34;角色&#34;作为单独的字符串请参阅Apache OGNL page

上的OGNL文档