Я работаю над преобразованием службы REST/JSON из Coldfusion 9 в приложение Spring -MVC 3.1. Я использую Jackson (1.9.5) и MappingJacksonJsonConverter, которые Spring предоставляет, и настраиваю ObjectMapper, чтобы называть поля CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES.
Проблема, с которой я столкнулся, заключается в том, что наша устаревшая услуга создает "верблюжьей кейс для случая UPPER с символами подчеркивания" как имена свойств json. Потребители этого JSON, также написанные в ColdFusion, могут меньше заботиться о случае, но Джексон заботится о случае и бросает UnrecognizedPropertyExceptions.
Изучив все настройки, которые я мог бы получить от ObjectMapper - DeserializationConfig, DeserializerProvider и т.д., я оказался в очень грязном хаке, в котором я разбираю дерево JSON, выводя его с помощью настраиваемого JsonGenerator, который уменьшает регистр в поле имена, а затем анализирует его как объект.
MappingJacksonHttpMessageConverter mc = new MappingJacksonHttpMessageConverter() {
@Override
protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return this.getObjectMapper().readValue(translateToLowerCaseKeys(inputMessage.getBody()), getJavaType(clazz));
}
private byte[] translateToLowerCaseKeys(InputStream messageBody) throws IOException {
StringWriter sw = new StringWriter();
JsonGenerator lowerCaseFieldNameGenerator = new JsonGeneratorDelegate(this.getObjectMapper().getJsonFactory().createJsonGenerator(sw)) {
@Override
public void writeFieldName(String name) throws IOException, org.codehaus.jackson.JsonGenerationException {
delegate.writeFieldName(name.toLowerCase());
};
};
this.getObjectMapper().writeTree(lowerCaseFieldNameGenerator, this.getObjectMapper().readTree(messageBody));
lowerCaseFieldNameGenerator.close();
return sw.getBuffer().toString().getBytes();
}
};
Это решение кажется очень неэффективным. Существует решение, которое работает для ключей карты, но я не смог найти аналогичное решение для имен полей.
Альтернативное решение состоит в том, чтобы иметь два сеттера, один из которых аннотирован с именем прежнего поля. Стратегия именования должна быть расширена, чтобы игнорировать эти поля, которые в моей ситуации прекрасны, поскольку объект mapper не будет иметь дело с любыми другими классами с стратегией UPPER_UNDERSCORE:
public class JsonNamingTest {
public static class CaseInsensitive extends LowerCaseWithUnderscoresStrategy {
public String translate(String in) {
return (in.toUpperCase().equals(in) ? in : super.translate(in));
}
}
public static class A {
private String testField;
public String getTestField() {
return testField;
}
public void setTestField(String field) {
this.testField = field;
}
@JsonProperty("TEST_FIELD")
public void setFieldAlternate(String field) {
this.testField = field;
}
}
@Test
public void something() throws Exception {
A test = new A();
test.setTestField("test");
ObjectMapper mapper = new ObjectMapper().setPropertyNamingStrategy(new CaseInsensitive());
assertEquals("{\"test_field\":\"test\"}", mapper.writeValueAsString(test));
assertEquals("test", mapper.readValue("{\"test_field\":\"test\"}", A.class).getTestField());
assertEquals("test", mapper.readValue("{\"TEST_FIELD\":\"test\"}", A.class).getTestField());
}
}
Это более удобно, чем предыдущее решение, но для каждого поля потребуются два аннотированных сеттера - один для нового формата, один для поддержки устаревшего формата.
Есть ли у кого-нибудь способ сделать Джексона без учета регистра для десериализации имен полей или, если на то пошло, принимать множественные псевдонимы для имени поля?