【问题标题】:How to access default jackson serialization in a custom serializer如何在自定义序列化程序中访问默认杰克逊序列化
【发布时间】:2023-04-08 02:51:01
【问题描述】:

我想创建一个自定义序列化程序,它会做一些工作,然后将其余部分留给默认序列化。

例如:

@JsonSerialize(using = MyClassSerializer.class)
public class MyClass {
  ...
}

public class MyClassSerializer extends JsonSerializer<MyClass> {
    @Override
    public void serialize(MyClass myClass, JsonGenerator generator, 
                          SerializerProvider provider) 
            throws JsonGenerationException, IOException {
        if (myClass.getSomeProperty() == someCalculationResult) {
            provider.setAttribute("special", true);
        }
        generator.writeObject(myClass);
    }  
}

考虑为聚合对象创建其他自定义序列化程序,这些聚合对象的行为基于“特殊”属性值而不同。但是,上面的代码不起作用,因为它毫无疑问地进入了无限递归。

一旦我设置了属性,有没有办法告诉杰克逊使用默认序列化?我真的不想像许多自定义序列化程序那样枚举所有属性,因为该类相当复杂,而且我不想每次更改类时都必须对序列化程序进行双重维护。

【问题讨论】:

标签: java json jackson


【解决方案1】:

BeanSerializerModifier 将为您提供对默认序列化的访问权限。

将默认序列化程序注入自定义序列化程序

public class MyClassSerializer extends JsonSerializer<MyClass> {
    private final JsonSerializer<Object> defaultSerializer;

    public MyClassSerializer(JsonSerializer<Object> defaultSerializer) {
        this.defaultSerializer = checkNotNull(defaultSerializer);
    }

    @Override
    public void serialize(MyClass myclass, JsonGenerator gen, SerializerProvider provider) throws IOException {
        if (myclass.getSomeProperty() == true) {
            provider.setAttribute("special", true);
        }
        defaultSerializer.serialize(myclass, gen, provider);
    }
}

MyClass 创建一个BeanSerializerModifier

public class MyClassSerializerModifier extends BeanSerializerModifier {
    @Override
    public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
        if (beanDesc.getBeanClass() == MySpecificClass.class) {
            return new MyClassSerializer((JsonSerializer<Object>) serializer);
        }
        return serializer;
    }
}

注册序列化器修饰符

ObjectMapper om = new ObjectMapper()
        .registerModule(new SimpleModule()
                .setSerializerModifier(new MyClassSerializerModifier()));

【讨论】:

  • 这消除了课堂上@JsonSerialize的需要。
  • 这个解决方案还有效吗?我尝试使用 Jackson 2.5.0 和 2.8.1 并得到: ...JsonMappingException: Class dto.ResponseSerializer has no default (no arg) constructor at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:282 ) 在 com.fasterxml.jackson.databind.SerializerProvider.reportMappingProblem(SerializerProvider.java:1145) 在 com.fasterxml.jackson.databind.SerializerProvider 的 com.fasterxml.jackson.databind.SerializerProvider.mappingException(SerializerProvider.java:1123)。 _createAndCacheUntypedSerializer(SerializerProvider.java:1227)
  • @Billybong,从你的类中删除 @JsonSerialize(using = MyClassSerializer.class) 注释。
  • @SamB.hi,我正在使用答案中描述的第一种方法。我收到一个编译错误,提示 checkNotNull 无法解析。我为特定类型添加了自定义序列化程序。但是自定义序列化程序无法访问默认序列化程序实例。它是空的!你能帮我如何获得访问权限。 return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.serializerByType(TreeNode.class, new TreeNodeJsonSerializer());
  • 这对我不起作用。尽管 custom-serializer 的 serialize() 方法和其中的 setAttribute() 被调用,但添加(或修改)的属性不会反映在最终的 json 输出中。 (顺便说一句,setAttribute() 可以添加一些额外的字段还是只修改现有的?)
【解决方案2】:
@JsonSerialize(using = MyClassSerializer.class)
public class MyClass {
...
}

public class MyClassSerializer extends JsonSerializer<MyClass> {
    @Override
     public void serialize(MyClass myClass, JsonGenerator generator, 
                      SerializerProvider provider) 
        throws JsonGenerationException, IOException {
        if (myClass.getSomeProperty() == someCalculationResult) {
            provider.setAttribute("special", true);
        } else {
            provider.defaultSerializeValue(myClass, generator);
        }
    }  
}

如果你只是像往常一样写一个对象,使用上面的

【讨论】:

  • 这不会返回 stackOverflowError 吗?
  • @PranjalGore 不,你为什么会这样认为?
  • 您正在调用 provider.defaultSerializer() 来对使用 MyClassSerialzer 进行序列化的 MyClass 对象进行序列化。现在是不是不可能默认jackson序列化器在类上遇到@JsonSerialize注解,调用MyClassSerialzer的serialize方法?这可能会形成一个无休止的循环。
  • 不,我调用的是 provider.defaultSerializeValue(),它与 ​​defaultSerializer() 不同,这只是检查是否有条件然后做一些特殊的事情,否则在序列化值方面执行默认值!它不会形成无休止的循环试试吧!
  • 这确实会引发 StackOverfliwError
【解决方案3】:

如果这是您想要进行的唯一更改,您可以使用 @JsonGetter 而不是使用自定义序列化程序。

public class MyClass{

    @JsonGetter("special")
    protected boolean getSpecialForJackson() {
        return myClass.getSomeProperty() == someCalculationResult;
    }

}

【讨论】:

  • 谢谢@Alex,但我正在设置一个属性标志以在序列化的后续步骤中访问。我试图序列化的不是标志本身。
【解决方案4】:

要添加到所选答案,序列化程序实现可能还必须实现ContextualSerializerResolvableSerializer 接口。请在此处查看相关问题 https://github.com/FasterXML/jackson-dataformat-xml/issues/259

public class MyClassSerializer extends JsonSerializer<MyClass>
    implements ContextualSerializer, ResolvableSerializer {
private final JsonSerializer<Object> defaultSerializer;

public MyClassSerializer(JsonSerializer<Object> defaultSerializer) {
    this.defaultSerializer = checkNotNull(defaultSerializer);
}

@Override
public void serialize(MyClass myclass, JsonGenerator gen, SerializerProvider provider)
        throws IOException {
    if (myclass.getSomeProperty() == true) {
        provider.setAttribute("special", true);
    }
    defaultSerializer.serialize(myclass, gen, provider);
}

@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
        throws JsonMappingException {
    if (defaultSerializer instanceof ContextualSerializer) {
        JsonSerializer<?> contextual = ((ContextualSerializer)defaultSerializer).createContextual(prov, property);
        return new MyClassSerializer((JsonSerializer<Object>)contextual);
    }
    return new MyClassSerializer(defaultSerializer);
}

@Override
public void resolve(SerializerProvider provider) throws JsonMappingException {
    if (defaultSerializer instanceof ResolvableSerializer) {
        ((ResolvableSerializer)defaultSerializer).resolve(provider);
    }
}

}

【讨论】:

    猜你喜欢
    • 2016-04-09
    • 2016-02-19
    • 1970-01-01
    • 1970-01-01
    • 2016-12-18
    • 1970-01-01
    • 2016-01-08
    • 1970-01-01
    • 2018-07-21
    相关资源
    最近更新 更多