Java 具有回退到默认序列化的自定义序列化程序
我有一个例子,对象可能需要以不同的方式序列化。具体地说,REST端点接受一个标头,如果标头为true,我们将以一种方式序列化,如果为false,我们将以另一种方式序列化。我认为如果我们将header属性放在对象中,那么定制序列化程序可能非常适合这种情况,但是我在使用它时遇到了一些麻烦 (请注意,由于使用了一些泛型以及代码的结构方式,仅仅创建一个不同的对象就需要大量的重新分解,因此我正在尝试找到一个更快的解决方案。) 例如,我的序列化程序如下所示:Java 具有回退到默认序列化的自定义序列化程序,java,serialization,jackson,Java,Serialization,Jackson,我有一个例子,对象可能需要以不同的方式序列化。具体地说,REST端点接受一个标头,如果标头为true,我们将以一种方式序列化,如果为false,我们将以另一种方式序列化。我认为如果我们将header属性放在对象中,那么定制序列化程序可能非常适合这种情况,但是我在使用它时遇到了一些麻烦 (请注意,由于使用了一些泛型以及代码的结构方式,仅仅创建一个不同的对象就需要大量的重新分解,因此我正在尝试找到一个更快的解决方案。) 例如,我的序列化程序如下所示: public class FooSerializ
public class FooSerializer extends StdSerializer<Foo>
{
@Override
public void serialize(
final Foo foo,
final JsonGenerator jsonGenerator,
final SerializerProvider serializerProvider)
throws IOException
{
if (foo.isFlattened())
{
jsonGenerator.writeObject(flatten(foo));
}
else
{
jsonGenerator.writeObject(foo);
}
}
private Map<String,Object> flatten(Foo foo)
{
// ... some logic that builds the Map...
}
}
问题几乎立即变得显而易见:jsonGenerator.writeObject
调用ObjectMapper
来序列化对象。。。这将再次调用我的序列化程序,最终给我们一个可爱的无限循环和堆栈溢出
我能想到的解决这个问题的唯一方法是使用反射,循环对象的属性,并使用jsonggenerator
将它们写入新对象。这感觉太过分了,尤其是当杰克逊能够为我做到这一点的时候。问题是我找不到方法告诉ObjectMapper
忽略类上的序列化程序
有更好的方法吗?我想我可以创建一个自定义的
ObjectMapper
,它以某种方式忽略类上注释的序列化程序,并将其用作jsonGenerator
的编解码器。。。但是不能完全确定在对象映射器上拉什么样的杠杆,正如怀疑的那样,有一种方法可以禁用特定的注释。在本例中,我想禁用Foo
类上的JsonSerialize
注释。所以,我这样做是为了打破无限循环:
public class FooSerializer extends StdSerializer<Foo>
{
@Override
public void serialize(
final Foo foo,
final JsonGenerator jsonGenerator,
final SerializerProvider serializerProvider)
throws IOException
{
ObjectMapper mapper = new ObjectMapper();
JacksonAnnotationIntrospector annotationIntrospector =
new JacksonAnnotationIntrospector()
{
@Override
protected <A extends Annotation> A _findAnnotation(
final Annotated annotated,
final Class<A> annotationClass)
{
if (annotated.hasAnnotation(JsonSerialize.class) &&
annotated.getRawType() == Foo.class)
{
return null;
}
return super._findAnnotation(annotated, annotationClass);
}
};
mapper.setAnnotationIntrospector(annotationIntrospector);
jsonGenerator.setCodec(mapper);
if (foo.isFlattened())
{
jsonGenerator.writeObject(flatten(foo));
}
else
{
jsonGenerator.writeObject(foo);
}
}
private Map<String,Object> flatten(Foo foo)
{
// ... some logic that builds the Map...
}
}
公共类FooSerializer扩展StdSerializer
{
@凌驾
公共无效序列化(
最后的福福,
最终JsonGenerator JsonGenerator,
最终序列化提供程序(序列化提供程序)
抛出IOException
{
ObjectMapper mapper=新的ObjectMapper();
JacksonAnnotation内省者Annotation内省者=
新JacksonAnnotationInterspector()
{
@凌驾
受保护的A_findAnnotation(
最终注释,
最终类注释(类)
{
if(annotated.hasnotation(JsonSerialize.class)&&
注释的.getRawType()==Foo.class)
{
返回null;
}
返回super.\u findAnnotation(注释,annotationClass);
}
};
mapper.SetAnnotationIntroSector(注释IntroSector);
setCodec(映射器);
if(foo.isplatten())
{
writeObject(flatte(foo));
}
其他的
{
jsonggenerator.writeObject(foo);
}
}
私人地图展平(Foo-Foo)
{
//…一些构建地图的逻辑。。。
}
}
Jackson
允许以多种不同的方式注册自定义序列化程序。其中之一是使用com.fasterxml.jackson.databind.ser.BeanSerializerModifier
类,该类允许创建自定义序列化程序,但如果需要,我们也可以使用那里提供的基本序列化程序
上面的问题看起来像是OOP
问题的一个很好的例子,我们可以将POJO
解耦,这是逻辑和序列化过程。Serialiser不应该知道如何将对象转换为Map
。它应该只有序列化逻辑。让我们介绍一下POJO
和serialiser使用的接口:
interface Flattenable {
boolean isFlattened();
Map<String, Object> flatten();
}
class Foo implements Flattenable {
private boolean flattened;
private int id;
public Foo(boolean flattened, int id) {
this.flattened = flattened;
this.id = id;
}
@Override
public Map<String, Object> flatten() {
Map<String, Object> map = new LinkedHashMap<>();
map.put("id", getId());
map.put("random", ThreadLocalRandom.current().nextDouble());
return map;
}
@Override
public boolean isFlattened() {
return flattened;
}
public void setFlattened(boolean flattened) {
this.flattened = flattened;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
当我们有POJO
模型和序列化器时,我们只需要配置ObjectMapper
并尝试使用我们的解决方案:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
public class JsonFlattenApp {
public static void main(String[] args) throws Exception {
SimpleModule flattenModule = new SimpleModule("FlattenModule");
flattenModule.setSerializerModifier(new FlattenableBeanSerializerModifier());
ObjectMapper mapper = JsonMapper.builder()
.addModule(flattenModule)
.build();
System.out.println(mapper.writeValueAsString(new Foo(true, 1)));
System.out.println(mapper.writeValueAsString(new Foo(false, 2)));
}
}
class FlattenableBeanSerializerModifier extends BeanSerializerModifier {
@Override
public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
if (Flattenable.class.isAssignableFrom(beanDesc.getBeanClass())) {
return new FlattenableJsonSerializer(serializer);
}
return serializer;
}
}
您的解决方案看起来有点像黑客。对于每个序列化过程,它都将创建新的ObjectMapper
,并自定义实现JacksonAnnotationIntrospector
,从而禁用Foo
的自定义序列化程序。另外,通过jsonGenerator.setCodec(mapper)
覆盖编解码器可能有缺点,因为该过程中的所有其他对象都将由ObjectMapper
的此实例序列化。如果您需要为java8
、java8 Time
等类使用一些自定义或通用模块,那么您每次都必须在自定义序列化程序中重新注册它们。考虑使用矿山解决方案。
class FlattenableJsonSerializer extends JsonSerializer<Flattenable> {
private final JsonSerializer<Object> base;
public FlattenableJsonSerializer(JsonSerializer base) {
this.base = Objects.requireNonNull(base);
}
@Override
public void serialize(Flattenable value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value.isFlattened()) {
gen.writeObject(value.flatten());
} else {
base.serialize(value, gen, serializers);
}
}
}
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
public class JsonFlattenApp {
public static void main(String[] args) throws Exception {
SimpleModule flattenModule = new SimpleModule("FlattenModule");
flattenModule.setSerializerModifier(new FlattenableBeanSerializerModifier());
ObjectMapper mapper = JsonMapper.builder()
.addModule(flattenModule)
.build();
System.out.println(mapper.writeValueAsString(new Foo(true, 1)));
System.out.println(mapper.writeValueAsString(new Foo(false, 2)));
}
}
class FlattenableBeanSerializerModifier extends BeanSerializerModifier {
@Override
public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
if (Flattenable.class.isAssignableFrom(beanDesc.getBeanClass())) {
return new FlattenableJsonSerializer(serializer);
}
return serializer;
}
}
//1
{"id":1,"random":0.7818309762014325}
//2
{"flattened":false,"id":2}