2016-08-26 10 views
1

私は特定の形式、すなわち"[lower,upper)"でシリアル化する必要があるRangeを持っています。ジャクソンで範囲をシリアライズ

public class RangeSerializer extends StdSerializer<Range<?>> { 

    @Override 
    public void serialize(final Range<?> value, final JsonGenerator gen, final SerializerProvider provider) throws IOException { 
    if (value != null) { 
     gen.writeRaw('"'); 
     gen.writeRaw('['); 
     provider.defaultSerializeValue(value.lowerEndpoint(), gen); 
     gen.writeRaw(','); 
     provider.defaultSerializeValue(value.upperEndpoint(), gen); 
     gen.writeRaw(')'); 
     gen.writeRaw('"'); 
    } 
    } 

(つまり、実際にシリアライザは、このようなオープン/クローズの範囲、どちらかの端などではなくのための無限の範囲の可能性としてRangeの様々な可能性を扱う注:私は書かれているので、基本的なシリアライザを行うには私の質問の目的は関係ないので、コードを単純にするために削除しました)。

私の問題は、各クラスのデフォルトのシリアライザに戻って、間違った場所で引用符で終わることです。たとえば、私がのエントリが"[foo,bar)"の場合、それをシリアル化すると"["foo","bar")"が得られます。下位と上位のエンドポイント値を引用符なしに結果が必要です。

基本的なシリアライザのgen.writeString()は、それが既に文字列内にあることを認識しないため、追加の引用符があることを理解します。ジェネレーターにこれを知らせるための方法や、私がしようとしていることを達成するための代替方法がありますか?

Range<?>は実際には一般的なので、値のシリアル化をハードコーディングすることはできません。 Range<Integer>Range<String>Range<DateTime>、その他のもので動作する必要があります。

答えて

0

私は、単一のジェネレータを使用してすべての機能をシリアル化するアプローチを手に入れることができませんでした。 DelegatingJsonGeneratorを使用して特定の通話を呼び出すことができるかもしれませんが、私は以下の方法が大幅に簡単であると判断しました(わずかなパフォーマンス低下の代償を払って)。

私はこれをどのように動作させることができたかを示すSpockテストクラスです。

@Grab('com.fasterxml.jackson.core:jackson-databind:2.8.1') 
@Grab('org.spockframework:spock-core:1.0-groovy-2.4') 

import com.fasterxml.jackson.databind.ObjectMapper 
import com.fasterxml.jackson.databind.SerializationFeature 
import com.fasterxml.jackson.databind.annotation.JsonSerialize 
import com.fasterxml.jackson.databind.util.StdConverter 
import spock.lang.Specification 
import spock.lang.Unroll 

class RangeSerializationTest extends Specification { 

    static class Range<T> { 
     T lower 
     T upper 
    } 

    @JsonSerialize(converter = RangeConverter) 
    static interface RangeMixin { 
    } 

    static class RangeConverter extends StdConverter<Range, String> { 
     private static final mapper = new ObjectMapper().disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) 

     @Override 
     String convert(Range value) { 
      def lower = mapper.convertValue(value.lower, String) 
      def upper = mapper.convertValue(value.upper, String) 
      return "[$lower,$upper)" 
     } 
    } 


    @Unroll 
    def 'range of #lower.class'() { 
     given: 
     def mapper = new ObjectMapper() 
     mapper.addMixIn(Range, RangeMixin) 

     expect: 
     mapper.writeValueAsString(new Range(lower: lower, upper: upper)) == expectedJson 

     where: 
     lower   | upper   | expectedJson 
     'abc'   | 'def'   | '"[abc,def)"' 
     123   | 456   | '"[123,456)"' 
     new Date(123) | new Date(456) | '"[1970-01-01T00:00:00.123+0000,1970-01-01T00:00:00.456+0000)"' 
    } 
} 
+0

回答ありがとうございます。 Aコンバータは興味深い方法ですが、シリアライザやデシリアライザを登録できるのと同じ方法で登録することができますか、または注釈を付ける必要がありますか? Rangeクラス自体にアクセスすることはできません。その範囲は実際にマップのキーとして使用されているので、アノテーションベースのソリューションが私に役立つかどうかはわかりません。 – jgm

+0

Mixinを使用して、注釈をRangeクラスに追加します。私は答えを編集してどのように表示したのですか? mapperで設定された 'addMixin(...)'部分に注目してください。 –