gpt4 book ai didi

java - 如何使用 Jackson 和注释以不同方式序列化关联对象?

转载 作者:塔克拉玛干 更新时间:2023-11-03 03:17:42 27 4
gpt4 key购买 nike

给定以下类层次结构,我希望 Foo 根据它在我的类层次结构中使用的上下文进行不同的序列化。

public class Foo {
public String bar;
public String biz;
}

public class FooContainer {
public Foo fooA;
public Foo fooB;
}

当我序列化 FooContainer 时,我希望 biz 属性不出现在 fooB 中。所以输出看起来像下面这样。

{
"fooA": {"bar": "asdf", "biz": "fdsa"},
"fooB": {"bar": "qwer"}
}

我打算使用一些 JsonView,但必须在类的所有实例的映射器层应用它,并且这是依赖于上下文的。


在 Jackson 用户邮件列表上,Tatu 给出了最简单的解决方案(适用于 2.0),我现在可能最终会使用它。将赏金授予 jlabedo,因为答案是如何使用自定义注释扩展 Jackson 的绝佳示例。

public class FooContainer {
public Foo fooA;

@JsonIgnoreProperties({ "biz" })
public Foo fooB;
}

最佳答案

您可以使用 JsonViews 将自定义序列化程序与自定义属性过滤器结合使用。这是一些使用 Jackson 2.0 的代码

定义自定义注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface FilterUsingView {
Class<?>[] value();
}

定义一些 View :

// Define your views here
public static class Views {
public class Public {};
public class Internal extends Public{};
}

然后你可以这样写你的实体。请注意,您可以定义自己的注释而不是使用 @JsonView :

public class Foo {
@JsonView(Views.Public.class)
public String bar;
@JsonView(Views.Internal.class)
public String biz;
}

public class FooContainer {
public Foo fooA;
@FilterUsingView(Views.Public.class)
public Foo fooB;
}

然后,这里是代码开始的地方:)首先是您的自定义过滤器:

public static class CustomFilter extends SimpleBeanPropertyFilter {

private Class<?>[] _nextViews;

public void setNextViews(Class<?>[] clazz){
_nextViews = clazz;
}

@Override
public void serializeAsField(Object bean, JsonGenerator jgen,
SerializerProvider prov, BeanPropertyWriter writer)
throws Exception {

Class<?>[] propViews = writer.getViews();

if(propViews != null && _nextViews != null){
for(Class<?> propView : propViews){
System.out.println(propView.getName());
for(Class<?> currentView : _nextViews){
if(!propView.isAssignableFrom(currentView)){
// Do the filtering!
return;
}
}
}
}
// The property is not filtered
writer.serializeAsField(bean, jgen, prov);
}
}

然后一个自定义的 AnnotationIntrospector 将做两件事:

  1. 为任何 bean 启用您的自定义过滤器...除非在您的类中定义了另一个过滤器(所以您不能同时使用它们,如果您明白我的意思的话)
  2. 如果他发现一个 @FilterUsingView 注释,启用你的 CustomSerializer。

这是代码

public class CustomAnnotationIntrospector extends AnnotationIntrospector {
@Override
public Version version() {
return DatabindVersion.instance.version();
}

@Override
public Object findFilterId(AnnotatedClass ac) {
// CustomFilter is used for EVERY Bean, unless another filter is defined
Object id = super.findFilterId(ac);
if (id == null) {
id = "CustomFilter";
}
return id;
}

@Override
public Object findSerializer(Annotated am) {
FilterUsingView annotation = am.getAnnotation(FilterUsingView.class);
if(annotation == null){
return null;
}
return new CustomSerializer(annotation.value());
}
}

这是您的自定义序列化程序。它所做的唯一一件事就是将注释的值传递给自定义过滤器,然后让默认的序列化程序完成这项工作。

public class CustomSerializer extends JsonSerializer<Object> {

private Class<?>[] _activeViews;

public CustomSerializer(Class<?>[] view){
_activeViews = view;
}

@Override
public void serialize(Object value, JsonGenerator jgen,
SerializerProvider provider) throws IOException,
JsonProcessingException {

BeanPropertyFilter filter = provider.getConfig().getFilterProvider().findFilter("CustomFilter");
if(filter instanceof CustomFilter){
CustomFilter customFilter = (CustomFilter) filter;

// Tell the filter that we will filter our next property
customFilter.setNextViews(_activeViews);

provider.defaultSerializeValue(value, jgen);

// Property has been filtered and written, do not filter anymore
customFilter.setNextViews(null);
}else{
// You did not define a CustomFilter ? Well this serializer is useless...
provider.defaultSerializeValue(value, jgen);
}
}
}

终于!让我们把这些放在一起:

public class CustomModule extends SimpleModule {

public CustomModule() {
super("custom-module", new Version(0, 1, 0, "", "", ""));
}

@Override
public void setupModule(SetupContext context) {
super.setupModule(context);
AnnotationIntrospector ai = new CustomAnnotationIntrospector();
context.appendAnnotationIntrospector(ai);
}

}



@Test
public void customField() throws Exception {
FooContainer object = new FooContainer();
object.fooA = new Foo();
object.fooA.bar = "asdf";
object.fooA.biz = "fdsa";
object.fooB = new Foo();
object.fooB.bar = "qwer";
object.fooB.biz = "test";

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new CustomModule());

FilterProvider fp = new SimpleFilterProvider().addFilter("CustomFilter", new CustomFilter());
StringWriter writer = new StringWriter();

mapper.writer(fp).writeValue(writer, object);

String expected = "{\"fooA\":{\"bar\":\"asdf\",\"biz\":\"fdsa\"},\"fooB\":{\"bar\":\"qwer\"}}";

Assert.assertEquals(expected, writer.toString());
}

关于java - 如何使用 Jackson 和注释以不同方式序列化关联对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12676249/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com