更改房产的杰克逊活动视图

时间:2015-05-12 09:43:41

标签: java json jackson

使用Jackson,我希望能够在序列化属性时更改活动视图。

所以,如果我的应用程序使用3个视图,A,B& C,我可能希望类型T的属性p始终使用视图A进行序列化,无论活动视图ClassnullA.classB.class还是{{1 }}

我还没有找到任何内置于杰克逊的方法,所以我打算自己创建这样做的课程。

在我重新发明轮子之前,杰克逊的正常注释和/或课程是否可以实现?

如果没有,我打算做类似以下的事情(注意:为了简化这个问题,我省略了许多配置选项和性能优化,这些都不是核心功能所必需的,但我会添加一旦核心工作;我偶尔也省略了可见性修饰符和类似的简洁):

  1. 制作以下注释,指出对于带注释的字段或方法,视图应从活动视图切换到C.class

    value
  2. 如果@Target({FIELD, METHOD}) @Retention(RUNTIME) public @interface JsonApplyView { Class<?> value(); } 应用于某个媒体资源,则会以某种方式更改@JsonApplyView_serializationView&amp;从getSerializationView() getActiveView() SerializerProvider value @JsonApplyView JsonApplyView.value appliedView _serializationView。{/ 1}}。{/}

    如果有更好/更正确的方法来更改活动视图,请告诉我。

    如果这是正确的方法,因为_serializationView是最终的,并且因为两个getter只返回BeanSerializerModifier,所以我必须创建一个modifySerializer() ContextualSerializer包装其参数序列化器使用JsonSerializer<?> createContextual(SerializerProvider sp, BeanProperty bp) { JsonApplyView apply = bp.getAnnotation(JsonApplyView.class); return apply == null || apply.value().equals(sp.getActiveView())) ? this : new ViewApplyingSerializer(this, apply.value()) ; }

    ViewApplyingSerializer

    final JsonSerializer<?> delegate; final Class<?> appliedView; 会在其构造函数中保存以下属性:

    ViewApplyingSerializer.serialize(Object o, JsonGenerator g, SerializerProvider p)

    p会执行以下操作之一:

    1. 复制delegate.serialize(o, g, copy(p, appliedView));

      SerializerProvider copy(SerializerProvider p, Class<?> appliedView)

      其中p返回_serializationView = appliedView的副本p

    2. 包裹delegate.serialize(o, g, wrap(p, appliedView));

      SerializerProvider wrap(SerializerProvider p, Class<?> appliedView)

      其中p返回_serializationView getSerializationView()getActiveView()&amp; appliedView全部返回ObjectWriter

    3. 使用全新的ow.writeValue(g, o);

      ow

      以某种方式获得SerializerProvider

  3. 我认为复制SerializerProvider是最好的,但我发现复制SerializerFactory并更改其有效视图的唯一方法需要传入SerializerFactory,我发现获得ObjectMapper的唯一方法是构建一个新方法,或者从_serializerFactory获取一个方法。我更喜欢使用当前SerializerProvider中的SerializerProvider来尽可能地模仿它,但该字段受到保护,我还没有找到它的任何吸气剂。复制或打包Class以使其具有不同视图opened的最简单方法是什么?

1 个答案:

答案 0 :(得分:2)

很久以前就问过这个问题,但我也在寻找类似的东西。我认为如果杰克逊能够自己支持这一点会很棒,因为在我看来这种情况很常见。

例如,假设您有一个视图层次结构,其中DetailView(高详细信息)继承自SummaryView(低详细信息),以及两个具有子父关系的对象。使用活动的DetailView序列化子对象时,您可能希望添加其父级的摘要,而不是其所有详细信息...然后您需要从DetailedView切换到SummaryView。

无论如何,不​​需要再次解释你已经知道的显而易见的事情,我最终做的是修改BeanSerializer,因为它只是序列化JsonView注释发生的bean。

@Override
public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
    return new JsonViewOverrideSerializer(serializer);
}

JsonViewOverrideSerializer保留原始bean序列化程序的引用。在上下文中发生了真正重要的事情:如果属性未使用@ApplyJsonView注释,则它将直接返回原始bean序列化程序。否则,它将bean序列化程序包含在上下文之后。像这样:

@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
    JsonSerializer<?> serializer = this.beanSerializer;

    if (serializer instanceof ContextualSerializer) {
        serializer = ((ContextualSerializer) serializer).createContextual(prov, property);
    }

    if (property != null) {
        ApplyJsonView applyJsonView = property.getAnnotation(ApplyJsonView.class);

        if (applyJsonView != null) {
            Class<?> jsonView = applyJsonView.value();                
            serializer = new JsonViewOverrideSerializer(serializer, jsonView);
        }
    }

    return serializer;
}

最后,这里是您想要覆盖SerializerProvider对象的棘手部分。我没有找到任何优雅的方式,所以我最终创建了SerializerProvider' by retrieving the SerializerFactory from the JsonGenerator codec, the ObjectMapper的新实例(我也是我)我宁愿从被覆盖的提供商那里检索它,但是很好......太糟糕了我无法访问它。)

public SerializerProvider overrideProvider(Class<?> overrideView, JsonGenerator gen, SerializerProvider provider) {
       return ((DefaultSerializerProvider) provider).createInstance(provider.getConfig().withView(overrideView), ((ObjectMapper) gen.getCodec()).getSerializerFactory(););
    });
}

(注意:为避免重新创建具有相同活动视图的提供程序,我会在提供程序的属性中保留所有已覆盖提供程序的映射。)

最后,在序列化对象之前,我让我的序列化程序查找其有效的提供程序:

@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
   SerializerProvider effectiveProvider = provider;

    if (provider.getActiveView() != null && overrideView != provider.getActiveView()) {                              
        effectiveProvider = overrideProvider(overrideView, gen, provider);
    }

    beanSerializer.serialize(value, gen, effectiveProvider);
}