2016-08-26 2 views
1

이라는 특정 형식으로 serialize해야하는 Range이 있습니다.Jackson과 범위 직렬화

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의 다양한 가능성을 처리 참고 : 내가 쓴 그래서 기본 시리얼을 수행하려면 코드를 간단하게 유지하기 위해 내 질문의 목적과 관련이 없습니다.)

내 문제는 각 클래스에 대한 기본 serializer로 돌아가서 잘못된 위치에 따옴표가있는 것입니다. 예를 들어 "[foo,bar)"이라는 항목이있는 Range<String>이있는 경우이를 직렬화하면 "["foo","bar")"이됩니다. 하단 및 상단 끝 값을 따옴표없이 결과가 필요합니다.

그 밖의 따옴표는 기본 serializer의 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 변환기는 일을하는 흥미로운 방법이지만, serializer 및 디시리얼라이저를 등록 할 수있는 것과 동일한 방식으로 등록 할 수 있습니까? 아니면 주석을 첨부해야합니까? Range 클래스 자체에 대한 액세스 권한이 없으며 범위가 실제로지도의 키로 사용되므로 주석 기반 솔루션이 여기에서 도움이되는지 확신 할 수 없습니다. – jgm

+0

Mixin을 사용하여 주석 클래스를 Range 클래스에 추가합니다. 방법을 보여줄 내 대답을 편집했습니다. 매퍼에 구성된'addMixin (...) '부분에 주목하자. –