将我的项目从 Jersey
移动到 Spring MVC
时遇到问题。
如何放宽 Jackson
中根值的大小写不敏感?
我想同时支持大写和小写。
我们有以下 Jackson
配置,它适用于属性和枚举但不适用于根值
spring.jackson.mapper.accept-case-insensitive-properties=true
spring.jackson.mapper.accept-case-insensitive-enums=true
在我的例子中,我无权访问 Car
类,因此不能使用任何 Jackson
注释,如 @JsonRootValue
来将 Car
类的名称更新为“car”
这是我面临的带有重现问题的测试的示例类。
在 jackson 库中配置放宽根名称不是很好吗?
public class CarTest {
public class Car {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Test
public void testCarRootValueCaseSensitivity() throws IOException {
Car car = new Car();
car.setName("audi");
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
objectMapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
String carAsString = objectMapper.writeValueAsString(car);
System.out.println(carAsString);
// Works fine with out any exception as the root value Car has captital 'C'
objectMapper.readValue("{\"Car\":{\"name\":\"audi\"}}", Car.class);
// Throws exception when lower case 'c' is provided than uppercase 'C'
objectMapper.readValue("{\"car\":{\"name\":\"audi\"}}", Car.class);
}
}
最佳答案
当你查看抛出的异常时:
Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Root name 'car' does not match expected ('Car') for type [simple type, class com.example.Car]
at [Source: (String)"{"car":{"name":"audi"}}"; line: 1, column: 2] (through reference chain: com.example.Car["car"])
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
at com.fasterxml.jackson.databind.DeserializationContext.reportPropertyInputMismatch(DeserializationContext.java:1477)
at com.fasterxml.jackson.databind.DeserializationContext.reportPropertyInputMismatch(DeserializationContext.java:1493)
at com.fasterxml.jackson.databind.ObjectMapper._unwrapAndDeserialize(ObjectMapper.java:4286)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4200)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3205)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3173)
您可以注意到 _unwrapAndDeserialize
方法名称。
在这个方法中你可以找到这段代码:
String actualName = p.getCurrentName();
if (!expSimpleName.equals(actualName)) {
ctxt.reportPropertyInputMismatch(rootType, actualName,
"Root name '%s' does not match expected ('%s') for type %s",
actualName, expSimpleName, rootType);
}
无法配置此行为,因为总是使用 equals
方法。
如果你真的想用不区分大小写的模式解析它,你可以覆盖 _unwrapAndDeserialize
方法并将 equals
替换为 equalsIgnoreCase
。示例类:
class CaseInsensitiveObjectMapper extends ObjectMapper {
protected Object _unwrapAndDeserialize(JsonParser p, DeserializationContext ctxt, DeserializationConfig config, JavaType rootType, JsonDeserializer<Object> deser) throws IOException {
PropertyName expRootName = config.findRootName(rootType);
// 12-Jun-2015, tatu: Should try to support namespaces etc but...
String expSimpleName = expRootName.getSimpleName();
if (p.getCurrentToken() != JsonToken.START_OBJECT) {
ctxt.reportWrongTokenException(rootType, JsonToken.START_OBJECT,
"Current token not START_OBJECT (needed to unwrap root name '%s'), but %s",
expSimpleName, p.getCurrentToken());
}
if (p.nextToken() != JsonToken.FIELD_NAME) {
ctxt.reportWrongTokenException(rootType, JsonToken.FIELD_NAME,
"Current token not FIELD_NAME (to contain expected root name '%s'), but %s",
expSimpleName, p.getCurrentToken());
}
String actualName = p.getCurrentName();
if (!expSimpleName.equalsIgnoreCase(actualName)) {
ctxt.reportPropertyInputMismatch(rootType, actualName,
"Root name '%s' does not match expected ('%s') for type %s",
actualName, expSimpleName, rootType);
}
// ok, then move to value itself....
p.nextToken();
Object result = deser.deserialize(p, ctxt);
// and last, verify that we now get matching END_OBJECT
if (p.nextToken() != JsonToken.END_OBJECT) {
ctxt.reportWrongTokenException(rootType, JsonToken.END_OBJECT,
"Current token not END_OBJECT (to match wrapper object with root name '%s'), but %s",
expSimpleName, p.getCurrentToken());
}
if (config.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) {
_verifyNoTrailingTokens(p, ctxt, rootType);
}
return result;
}
}
我从 ObjectMapper
类(版本 2.10.1
)复制了整个方法,如果您要升级 Jackson
版本,您需要检查此方法的实现方式并在需要时替换它。
最后,您可以在测试中使用这个新类型:
ObjectMapper objectMapper = new CaseInsensitiveObjectMapper();
另见:
关于java - 如何将 JSON 字符串反序列化为放宽根值区分大小写的对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63161908/