使用Jackson,我希望能够在序列化属性时更改活动视图。
所以,如果我的应用程序使用3个视图,A,B& C,我可能希望类型T的属性p始终使用视图A进行序列化,无论活动视图Class
是null
,A.class
,B.class
还是{{1 }}
我还没有找到任何内置于杰克逊的方法,所以我打算自己创建这样做的课程。
在我重新发明轮子之前,杰克逊的正常注释和/或课程是否可以实现?
如果没有,我打算做类似以下的事情(注意:为了简化这个问题,我省略了许多配置选项和性能优化,这些都不是核心功能所必需的,但我会添加一旦核心工作;我偶尔也省略了可见性修饰符和类似的简洁):
制作以下注释,指出对于带注释的字段或方法,视图应从活动视图切换到C.class
:
value
如果@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
会执行以下操作之一:
复制delegate.serialize(o, g, copy(p, appliedView));
:
SerializerProvider copy(SerializerProvider p, Class<?> appliedView)
其中p
返回_serializationView = appliedView
的副本p
包裹delegate.serialize(o, g, wrap(p, appliedView));
:
SerializerProvider wrap(SerializerProvider p, Class<?> appliedView)
其中p
返回_serializationView
getSerializationView()
,getActiveView()
&amp; appliedView
全部返回ObjectWriter
使用全新的ow.writeValue(g, o);
ow
以某种方式获得SerializerProvider
我认为复制SerializerProvider
是最好的,但我发现复制SerializerFactory
并更改其有效视图的唯一方法需要传入SerializerFactory
,我发现获得ObjectMapper
的唯一方法是构建一个新方法,或者从_serializerFactory
获取一个方法。我更喜欢使用当前SerializerProvider
中的SerializerProvider
来尽可能地模仿它,但该字段受到保护,我还没有找到它的任何吸气剂。复制或打包Class
以使其具有不同视图opened
的最简单方法是什么?
答案 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);
}