11

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 つは従来の形式をサポートするためです。

フィールド名を逆シリアル化するためにジャクソンの大文字と小文字を区別しないようにする方法、またはフィールド名の複数のエイリアスを受け入れる方法に誰か出くわしましたか?

4

4 に答える 4

13

これは Jackson 2.5.0 で修正されました。

ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
于 2015-03-16T20:21:36.127 に答える
6

PropertyNamingStrategy を使用できます。

  class CaseInsensitiveNaming extends PropertyNamingStrategy {
    @Override
    public String nameForGetterMethod(MapperConfig<?> config,
         AnnotatedMethod method, String defaultName)
    {
      // case-insensitive, underscores etc. mapping to property names
      // so you need to implement the convert method in way you need.
      return convert(defaultName); 
    }
  }

  objectMapper.setPropertyNamingStrategy(new CaseInsensitiveNaming());
于 2012-04-09T20:50:38.687 に答える
5

Since you are using Jackson 1.x and not Jackson 2.x, you could use MixIns. From what I understand, you want deserialization to understand UPPER_CASE but you want to serialize in lower_case. Add a MixIn in the deserialization config but not in the serialization config, as such:

public class JsonNamingTest {   
    public static class A {
        private String testField;
        public String getTestField() {
            return testField;
        }
        public void setTestField(String field) {
            this.testField = field;
        }
    }

    public static class MixInA {
        @JsonProperty("TEST_FIELD")
        public void setTestField(String field) {}
    }

    @Test
    public void something() throws Exception {
        A test = new A();
        test.setTestField("test");
        ObjectMapper mapper = new ObjectMapper();
        mapper.getDeserializationConfig().addMixInAnnotations(A.class, MixInA.class);
        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());
    }
}

However, you can't do that with Jackson 2.x: mixIns are shared by the serialization and deserialization config. I have yet to find a way to handle aliases with Jackson 2.x :(

于 2012-05-31T14:48:36.867 に答える