Java Jackson如何在自定义序列化程序中为对象使用默认序列化程序

时间:2018-07-11 16:02:06

标签: java json spring-boot serialization jackson

我有一个自定义的超类,称为Resource,我希望每次对Resource实例(或子类实例)进行序列化时,都会向JSON添加两个额外的字段。我正在使用Spring Boot(如果重要)和Jackson。我的想法是为Jackson定义一个自定义序列化程序,但我当前的实现存在一些问题。

@JsonComponent
public class CustomJsonSerializer extends JsonSerializer<Resource> {
    @Override
    public void serialize(Resource resource, JsonGenerator jsonGenerator,
                      SerializerProvider serializerProvider) throws IOException {

        jsonGenerator.writeStartObject();
        jsonGenerator.writeStringField("type", "special");

        jsonGenerator.writeFieldName("data");
        jsonGenerator.writeObject(resource);

        jsonGenerator.writeFieldName("links");
        jsonGenerator.writeObject(resource.getId());

        jsonGenerator.writeEndObject();
    }
}

不幸的是,调用writeObject(resource)仅重新进入了我的CustomJsonSerializer,进入了递归循环。

重要的一点是,资源可以嵌套,因此用默认的序列化程序简单地序列化资源还不够。

我正在寻找的是一种writeAllFields(resource)方法。当然可以使用反射自己实现,但是我忽略了所有Jackson注释,这也是我要避免的事情。

编辑:

Resource类是一个超类型,预期的行为是Resource的所有子类都要通过此序列化器进行处理(这种情况已经发生了)。

public class Resource {
    String id;

    protected Resource() {}

    public String getId() {
        return id;
    }
}

public class Item extends Resource {
    // Fields and methods, nothing special
}

编辑2: 万一重要,这就是我在Spring Boot中注册序列化器的方式:

    @Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    SimpleModule m = new SimpleModule();
    m.addSerializer(Resource.class, new CustomJsonSerializer());
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder().modules(m);
    converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}

2 个答案:

答案 0 :(得分:1)

您的帖子标题声明您希望从自定义序列化程序中访问默认的序列化程序,因为以下帖子中的人正在尝试这样做:

https://groups.google.com/forum/#!topic/jackson-user/cFQhuyw8vpw

如果滚动到底部,那家伙会说:

  

访问否则将被创建/用作的唯一方法   标准串行器将注册BeanSerializerModifier,并且   覆盖modifySerializer的处理。

您还可以查看有关Jackson的高级序列化功能的本教程,其中该人员正在执行与您尝试执行的操作类似的操作:

http://www.baeldung.com/jackson-serialize-field-custom-criteria

但是,通过查看您的代码,我推断您正在尝试实现Resource类的子类型的多态序列化。

如果是这样,下面是一个简单的解决方案,希望它正是您要寻找的东西:


Resource.java

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = ResourceFoo.class, name = "foo"),
        @JsonSubTypes.Type(value = ResourceBar.class, name = "bar")
})
public abstract class Resource {

    private String id;

    @JsonProperty("links")
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

ResourceFoo.java

public class ResourceFoo extends Resource {

    private String fieldA;

    private String fieldB;

    public String getFieldA() {
        return fieldA;
    }

    public void setFieldA(String fieldA) {
        this.fieldA = fieldA;
    }

    public String getFieldB() {
        return fieldB;
    }

    public void setFieldB(String fieldB) {
        this.fieldB = fieldB;
    }
}

ResourceBar.java

public class ResourceBar extends Resource {

    private int fieldX;

    private int fieldY;

    public int getFieldX() {
        return fieldX;
    }

    public void setFieldX(int fieldX) {
        this.fieldX = fieldX;
    }

    public int getFieldY() {
        return fieldY;
    }

    public void setFieldY(int fieldY) {
        this.fieldY = fieldY;
    }
}

SerializerDemo.java

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class SerializerDemo {

    public static void main(String[] args) throws JsonProcessingException {

        ObjectMapper mapper = new ObjectMapper();
        mapper.enableDefaultTyping();

        ResourceFoo foo = new ResourceFoo();
        foo.setId("abc");
        foo.setFieldA("Apples");
        foo.setFieldB("Oranges");

        String fooJson = mapper
                .writerWithDefaultPrettyPrinter()
                .writeValueAsString(foo);

        System.out.println(fooJson);

        ResourceBar bar = new ResourceBar();
        bar.setId("xyz");
        bar.setFieldX(12);
        bar.setFieldY(238);

        String barJson = mapper
                .writerWithDefaultPrettyPrinter()
                .writeValueAsString(bar);

        System.out.println(barJson);
    }
}

SerializerDemo输出:

{
  "type" : "foo",
  "fieldA" : "Apples",
  "fieldB" : "Oranges",
  "links" : "abc"
}
{
  "type" : "bar",
  "fieldX" : 12,
  "fieldY" : 238,
  "links" : "xyz"
}

有关更多信息,Eugen Paraschiv还就此发表了一篇不错的小文章:

http://www.baeldung.com/jackson-inheritance

答案 1 :(得分:0)

这应该有效:

    @JsonComponent
    public static class CustomJsonSerializer extends JsonSerializer<Resource> {
        private void serializeFields(Resource bean, JsonGenerator gen, SerializerProvider provider)
                throws IOException {
            JavaType javaType = provider.constructType(Resource.class);
            BeanDescription beanDesc = provider.getConfig().introspect(javaType);
            JsonSerializer<Object> serializer = BeanSerializerFactory.instance.findBeanSerializer(provider,
                    javaType,
                    beanDesc);
            serializer.unwrappingSerializer(null).serialize(bean, gen, provider);
        }

        @Override
        public void serialize(Resource resource, JsonGenerator jsonGenerator,
                              SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeStartObject();
            jsonGenerator.writeStringField("type", "special");

            jsonGenerator.writeFieldName("data");
            jsonGenerator.writeStartObject();
            serializeFields(resource, jsonGenerator, serializerProvider);
            jsonGenerator.writeEndObject();

            jsonGenerator.writeFieldName("links");
            jsonGenerator.writeObject(resource.links());

            jsonGenerator.writeEndObject();
        }
    }