1

我有一个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>其他任何东西工作。

4

1 回答 1

0

我无法获得您使用单个生成器序列化所有工作的方法。您可能可以使用 aDelegatingJsonGenerator和 hook 某些调用,但我认为下面显示的方式要简单得多(以轻微的性能损失为代价)。

这是一个 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)"'
    }
}
于 2016-08-26T15:07:54.580 回答