[英]Custom serializer with fallback to default serialization
我有一個案例,object 可能需要以不同的方式進行序列化。 具體來說,REST 端點采用 header,如果 header 為真,我們以一種方式序列化,如果為假,我們以另一種方式序列化。 我認為如果我們將 header 屬性放在 object 中,自定義序列化程序可能是完美的,但是在使用它時遇到了一些麻煩。
(請注意,由於使用了一些 generics 以及代碼的結構方式,因此僅創建不同的 object 將需要大量重構,因此我正在嘗試找到更快的解決方案。)
例如,我的序列化程序如下所示:
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...
}
}
並且正在序列化的 class 將被相應地注釋:
@JsonSerialize(using = FooSerializer.class)
public class Foo
{
// ... things...
}
問題幾乎立即變得明顯: jsonGenerator.writeObject
調用ObjectMapper
來序列化 object... 然后它會再次調用我的序列化器,最終給我們一個可愛的無限循環和堆棧溢出。
我能想到的解決此問題的唯一選擇是使用反射,遍歷對象的屬性,然后使用 jsonGenerator 將它們寫入新的jsonGenerator
。 這感覺有點矯枉過正,尤其是當 Jackson 應該能夠為我做到這一點時。 問題是我找不到告訴ObjectMapper
忽略 class 上的序列化程序的方法。
有沒有更好的方法來做到這一點? 我想我可以創建一個自定義ObjectMapper
以某種方式忽略 class 上注釋的序列化程序,並將其用作jsonGenerator
的編解碼器...但不完全確定要拉上ObjectMapper
的杠桿。
正如懷疑的那樣,有一種方法可以禁用特定的注釋。 在這種情況下,我想禁用Foo
class 上的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...
}
}
Jackson
允許以多種不同方式注冊自定義序列化器。 其中之一是使用com.fasterxml.jackson.databind.ser.BeanSerializerModifier
class,它允許創建自定義的基本序列化器,但也可以使用,如果需要,我們也可以使用它。
上面的問題看起來像是OOP
問題的一個很好的例子,我們可以解耦POJO
,它的邏輯和序列化過程。 串行器不應該知道如何將 object 轉換為Map
。 它應該只有序列化邏輯。 下面介紹一下序列化器使用的POJO
和接口:
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;
}
}
現在,我們可以通過實現Flattenable
接口輕松處理其他類型。 序列化邏輯將是相同的。 自定義序列化程序可能如下所示:
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);
}
}
}
當我們有POJO
model 和序列化器時,我們只需要配置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;
}
}
上面的代碼打印了兩行:
//1
{"id":1,"random":0.7818309762014325}
//2
{"flattened":false,"id":2}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.