REST/JSON サービスを Coldfusion 9 から Spring-MVC 3.1 アプリケーションに変換する作業を行っています。Jackson (1.9.5) と Spring が提供する MappingJacksonJsonConverter を使用しており、ObjectMapper をカスタマイズして CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES でフィールドに名前を付けています。
私が直面している問題は、レガシー サービスが json プロパティ名として「キャメル ケースからアンダースコア付きの UPPER ケース」を生成することです。同じく ColdFusion で記述されたこの JSON のコンシューマーは、大文字と小文字を気にする必要はありませんが、Jackson は大文字と小文字を区別し、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();
}
};
このソリューションは非常に非効率的です。マップのキーに対して機能する解決策がありますが、フィールド名に対して同様の解決策を見つけることができませんでした。
別の解決策は、2 つの setter を持ち、1 つにレガシー フィールド名の注釈を付けることです。これらのフィールドを無視するように命名戦略を拡張する必要があります。これは、オブジェクト マッパーが 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());
}
}
これは以前のソリューションよりも理想的ですが、フィールドごとに 2 つの注釈付きセッターが必要になります。1 つは新しい形式用で、もう 1 つは従来の形式をサポートするためです。
フィールド名を逆シリアル化するためにジャクソンの大文字と小文字を区別しないようにする方法、またはフィールド名の複数のエイリアスを受け入れる方法に誰か出くわしましたか?