【问题标题】:Serialize Range with Jackson用杰克逊序列化范围
【发布时间】:2016-08-26 10:55:30
【问题描述】:

我有一个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 的各种可能性,例如关闭/打开范围,两端无限范围的可能性等,但出于我的问题的目的,这无关紧要,所以我已经删除它以保持代码简单)。

我的问题是,通过回退到每个类的默认序列化程序,我最终会在错误的位置使用引号。例如,如果我有一个带有条目"[foo,bar)"Range&lt;String&gt;,那么在序列化它时我会获得"["foo","bar")"。我需要在上下端点值周围不带引号的结果。

我知道额外的引号是因为底层序列化程序中的gen.writeString() 没有意识到它已经在字符串中。有什么方法可以让生成器知道这一点,或者有另一种方法来实现我想要做的事情吗?

请注意,Range&lt;?&gt; 确实是通用的,因此我无法对值的序列化进行硬编码。它需要适用于Range&lt;Integer&gt;Range&lt;String&gt;Range&lt;DateTime&gt; 和其他任何东西。

【问题讨论】:

    标签: json jackson jackson2


    【解决方案1】:

    我无法获得您使用单个生成器序列化所有工作的方法。您也许可以使用 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)"'
        }
    }
    

    【讨论】:

    • 感谢您的回答。转换器是一种有趣的做事方式,但它是否可以像注册序列化器和反序列化器一样注册,还是必须附加注释?我无权访问 Range 类本身,并且该范围实际上被用作地图的键,所以我不确定基于注释的解决方案是否会帮助我。
    • 使用 mixin 将注解添加到 Range 类中。我已经编辑了我的答案以显示如何。注意映射器上配置的addMixin(...) 部分。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-03
    • 2020-12-24
    • 2015-04-07
    • 1970-01-01
    • 2012-01-26
    • 2013-11-20
    相关资源
    最近更新 更多