验证JSON请求模式

4

我有一个控制器类,其中包含这个方法:

    @RequestMapping(value = "/x", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<MyRepsonseClass> get(
        @ApiParam(value = "x", required = true) @Valid @RequestBody MyRequestClass request
    ) throws IOException {
        //yada yada my logic here
        return something;
    }

Json请求会自动映射到MyRequestClass.java中。这是该类的样子:
@lombok.ToString
@lombok.Getter
@lombok.Setter
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@ApiModel(description = "description")
public class MyRequestClass {
    private List<SomeClass> attribute1;
    private SomeOtherClass attribute2;
    private YetAnotherClass attribute3;
}

这是一个有效的JSON请求示例:
{
    "attribute1": [
        {
            "key":"value"
        }
    ],
    "attribute3": {
        "key":"value"
    }
}

现在,我的要求是当请求中包含一个在 MyRequestClass.java 中不存在的属性时返回一个错误消息。
因此:
{
    "attribute1": [
        {
            "key":"value"
        }
    ],
    "attribute_that_doesnt_exist": {
        "key":"value"
    }
}

目前它没有抛出任何错误。相反,它只是没有将该属性映射到任何东西上。我能使用哪些注释来快速实现这个功能呢?谢谢。


假设您参考了这个示例,并尝试使用它建议的方法。 - Rajith Pemabandu
你是否正在使用 @JsonIgnoreProperties 注解? - Jayanand Raghuwanshi
如果你在 MyRequestClass 类上使用了 @JsonIgnoreProperties,那么它不会抛出任何异常。如果你将其移除,则会抛出异常。 - pvpkiran
@JayanandRaghuwanshi 不,我不是。 - adbar
@pvpkiran 没有使用那个注释。我编辑了我的帖子,包括我目前正在使用的注释。 - adbar
@RajithPemabandu 抱歉,这并没有完全满足我的需求。 - adbar
2个回答

3
创建自定义反序列化器:
public class MyRequestClassDeserializer extends JsonDeserializer<MyRequestClass> {
    @Override
    public MyRequestClass deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException, JsonProcessingException {
        MyRequestClass mrc = new MyRequestClass();
        ObjectMapper mapper = new ObjectMapper();
        JsonToken currentToken = null;
        while((currentToken = jsonParser.nextValue()) != null) {
            if(currentToken.equals(JsonToken.END_OBJECT) 
                    || currentToken.equals(JsonToken.END_ARRAY))
                continue;
            String currentName = jsonParser.getCurrentName();
            switch(currentName) {
                case "attribute1":
                    List<SomeClass> attr1 = Arrays.asList(mapper.readValue(jsonParser, SomeClass[].class));
                    mrc.setAttribute1(attr1);
                    break;
                case "attribute2":
                    mrc.setAttribute2(mapper.readValue(jsonParser, SomeOtherClass.class));
                    break;
                case "attribute3":
                    mrc.setAttribute3(mapper.readValue(jsonParser, YetAnotherClass.class));
                    break;
                // <cases for all the other expected attributes>
                default:// it's not an expected attribute
                    throw new JsonParseException(jsonParser, "bad request", jsonParser.getCurrentLocation());
            }
        }
        return mrc;
    }
}  

请将以下注释添加到您的类中:@JsonDeserialize(using=MyRequestClassDeserializer.class) 唯一的“问题”是手动反序列化json可能会很麻烦。 我本来可以为您的情况编写完整的代码,但我现在还不够好。 我可能会在将来更新答案。
编辑:完成,现在是好使的代码。 我觉得它更加容易了解。

谢谢,我会尝试这种方法一段时间,并告诉您我的结果。 - adbar
1
我修改了代码,使其能够与您的示例实际运行(顺便说一下,在您的示例JSON中有一个错误,在第二个属性中,冒号在属性名称内部)。 - nonzaprej
这实际上比我预期的效果要好。它以类似的方式自动验证属性1、属性2和属性3字段中的所有字段。 - adbar

1

你尝试过这个注解吗 @JsonIgnoreProperties(ignoreUnknown = false)

如果是Spring Boot的话,更好的做法是在application.properties(yaml)中使用这个属性。

spring.jackson.deserialization.fail-on-unknown-properties=true

我刚刚尝试了注释,但出于某种原因它没有起作用。这个属性确实有效,但我猜它是针对整个应用程序全局的,也许他只需要在一个或一些端点上返回有关额外JSON属性的错误,而不是所有端点。 - nonzaprej
你是对的。它曾经可以工作,我不知道是否是一个bug,现在它已经不能再工作了。 https://dev59.com/h53ha4cB1Zd3GeqPSlNC - so-random-dude

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接