用杰克逊序列化范围

时间:2016-08-26 10:55:30

标签: json jackson jackson2

我有Range我需要以某种格式序列化,即"[lower,upper)"。为此,我编写了一个基本的序列化器:

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的各种可能性,例如封闭/开放范围,两端无限范围的可能性等等,但出于我的问题的目的,不相关,所以我删除它以保持代码简单。

我的问题是,通过回退到每个类的默认序列化程序,我最终在错误的地方引用了引号。例如,如果我有Range<String>条目"[foo,bar)",那么在序列化时我会获得"["foo","bar")"。我需要在没有引号的情况下围绕下端点和上端点值。

我理解附加引号是因为底层序列化程序中的gen.writeString()没有意识到它已经在字符串中。有没有办法让发电机知道这个,或者另一种方法来实现我试图做的事情?

请注意,Range<?>确实是通用的,因此我无法对值的序列化进行硬编码。它需要适用于Range<Integer>Range<String>Range<DateTime>以及其他任何内容。

1 个答案:

答案 0 :(得分: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)"'
    }
}