使用Jackson,我希望能够在序列化属性时更改活动视图。
因此,如果我的应用程序使用3个视图A,B和C,我可能希望类型T的属性p始终使用视图A进行序列化,而不管活动视图Class
是null
,A.class
,B.class
或C.class
。
我还没有找到任何内置于Jackson的方法,所以我计划制作自己的类来实现此功能。
在我可能重新发明轮子之前,使用普通的Jackson注释和/或类是否可以做到这一点?
如果不是这样,我计划执行以下操作(注意:为简化此问题,我省略了许多配置选项和性能优化,这些功能对于核心功能不是必需的,但是我将在核心工作后再添加) ;为了简明起见,我偶尔还会省略可见性修饰符等):
进行以下注释,指示对于带注释的字段或方法,应将视图从活动视图切换到value
:
@Target({FIELD, METHOD})
@Retention(RUNTIME)
public @interface JsonApplyView {
Class<?> value();
}
如果将
@JsonApplyView
应用于属性,则以某种方式将_serializationView
中getSerializationView()
,getActiveView()
和SerializerProvider
的值从原始活动视图更改为value
中的@JsonApplyView
(我们将其称为JsonApplyView.value
appliedView
)。如果有更好/更正确的方法来更改活动视图,请告诉我。
如果这是正确的方法,则由于
_serializationView
是最终的,并且由于两个getter都返回_serializationView
,因此我必须创建一个BeanSerializerModifier
,其modifySerializer()
用ContextualSerializer
将其参数序列化器包装为ViewApplyingSerializer
,并且: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.serialize(Object o, JsonGenerator g, SerializerProvider p)
将以下属性保存在其构造函数中:final JsonSerializer<?> delegate;
final Class<?> appliedView;
p
将执行以下操作之一:复制
SerializerProvider copy(SerializerProvider p, Class<?> appliedView)
:delegate.serialize(o, g, copy(p, appliedView));
其中
p
返回_serializationView = appliedView
的副本,其p
包装
SerializerProvider wrap(SerializerProvider p, Class<?> appliedView)
:delegate.serialize(o, g, wrap(p, appliedView));
其中
p
返回围绕_serializationView
的包装,其getSerializationView()
,getActiveView()
和appliedView
均返回ObjectWriter
使用全新的
ow
ow.writeValue(g, o);
以某种方式获得
SerializerProvider
我想复制
SerializerProvider
会是最好的,但是我发现复制SerializerFactory
并更改其活动视图的唯一方法是传递SerializerFactory
,并且这是我发现的唯一方法。获得ObjectMapper
是构造一个新的,或从_serializerFactory
获得一个。我更喜欢使用当前SerializerProvider
中的SerializerProvider
来尽可能地模仿它,但是该字段是受保护的,而且我还没有发现任何吸气剂。复制或包装Class
使其具有不同视图的最简单方法是什么? 最佳答案
这是很久以前问过的,但我也在寻找类似的方法。我认为,如果杰克逊能够自己支持这一点,那就太好了,因为在我看来,这是一个非常频繁的案例。
例如,假设您有一个视图层次结构,其中DetailView(高细节)从SummaryView(低细节)继承,以及两个具有父子关系的对象。当使用活动的DetailView序列化子对象时,您可能希望添加其父对象的摘要,而不必添加其所有详细信息……然后您需要从DetailsView切换到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);
}
关于java - 更改属性的 jackson Activity View ,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30187119/