Jackson 自定义序列化程序,用于某些字段的自定义行为,同时对其余字段具有默认行为
Jackson Custom serializer for custom behaviour of some fields while having default behaviour for rest of the fields
例如,我有一个如下定义的 POJO,带有 jackson-core
和 jackson-databind
(版本 2.8.3)注释,为简洁起见省略了 getter 和 setter。
class Sample {
private String foo;
private String bar;
private Map<String, Map<String, Object>> data;
}
我想编写一个自定义序列化程序,它采用 POJO 并生成
{
"foo":"val",
"bar":"val2",
"data_1": {
"someInt":1
},
"data_2": {
"someBoolean":true
}
}
这里的data_1
和data_2
是mainMap
的key,它们的内部属性是由它们的子图(嵌套图)组成的。此外,实际的 属性 数据根本不应出现在结果 JSON 中。
请注意,foo 和 bar 是字段示例,实际上 pojo 有 15+ 个字段。
请更正您的 class 变量 data,
应该是这样
private Map<String,Map<String, Object>> data;
然后写一个方法这样序列化:
public static void serializeSample() {
ObjectMapper mapper = new ObjectMapper();
Sample sample=new Sample();
sample.setBar("val2");
sample.setFoo("val");
Map<String, Map<String, Object>> sampleData=new HashMap<>();
Map<String, Object> data_3=new HashMap<>();
Map<String, Object> data_4=new HashMap<>();
data_3.put("someInt", 1);
data_4.put("someBoolean", Boolean.TRUE);
sampleData.put("data_1", data_3);
sampleData.put("data_2", data_4);
sample.setData(sampleData);
try {
mapper.writeValue(new File("log.txt"), sample);
} catch (JsonGenerationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JsonMappingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
我认为这应该可以正常工作
您将需要一个自定义序列化程序,它遍历父级 Map
。对于每个嵌套的 Map
,您可以简单地使用 writeObjectField
和键,值作为字段名称和值。
您还需要自动序列化所有其他字段。您只能在一个字段上设置自定义序列化程序,但您仍然会在 JSON 中获得一个 data
字段。您实际上想要提升数据的内容,使它们看起来像 Sample
的字段,这需要 Sample
的自定义序列化程序。我能想到的自动序列化除 data
字段以外的所有其他内容的唯一方法是使用反射
以下序列化程序将产生 JSON,如您的问题:
class SampleSerializer extends StdSerializer<Sample> {
private static final List<Field> sampleFields;
public SampleSerializer() { this(null); }
private SampleSerializer(Class<Sample> t) { super(t); }
static {
sampleFields = Arrays.stream(Sample.class.getDeclaredFields())
.filter(f -> !("data".equals(f.getName())))
.collect(toList());
}
@Override
public void serialize(Sample sample, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeStartObject();
for (Field field : sampleFields) {
try {
field.setAccessible(true);
jgen.writeObjectField(field.getName(), field.get(sample));
} catch (IllegalAccessException ignored) {
}
}
for (Entry<String, Map<String, Object>> entry : sample.getData().entrySet()) {
jgen.writeObjectField(entry.getKey(), entry.getValue());
}
jgen.writeEndObject();
}
}
您还必须指定 SampleSerializer
将用于 Sample
,例如通过注释 class:
@JsonSerialize(using = SampleSerializer.class)
class Sample {
我想出了一个更简单的方法来做到这一点,而无需使用自定义序列化程序;它与@JsonAnyGetter 和@JsonAnySetter 一起使用。这是一个完整的例子。我正在粘贴关于此处粘贴示例的答案,因为它可能对其他人有用。
class Sample {
private String foo;
private String bar;
private Map<String, Map<String, Object>> data = new LinkedHashMap<>();
@JsonAnyGetter
public Map<String, Map<String, Object>> getData() {
return data;
}
@JsonAnySetter
public void set(String key, Map<String, Object>) {
data.put(key, object);
}
}
例如,我有一个如下定义的 POJO,带有 jackson-core
和 jackson-databind
(版本 2.8.3)注释,为简洁起见省略了 getter 和 setter。
class Sample {
private String foo;
private String bar;
private Map<String, Map<String, Object>> data;
}
我想编写一个自定义序列化程序,它采用 POJO 并生成
{
"foo":"val",
"bar":"val2",
"data_1": {
"someInt":1
},
"data_2": {
"someBoolean":true
}
}
这里的data_1
和data_2
是mainMap
的key,它们的内部属性是由它们的子图(嵌套图)组成的。此外,实际的 属性 数据根本不应出现在结果 JSON 中。
请注意,foo 和 bar 是字段示例,实际上 pojo 有 15+ 个字段。
请更正您的 class 变量 data, 应该是这样
private Map<String,Map<String, Object>> data;
然后写一个方法这样序列化:
public static void serializeSample() {
ObjectMapper mapper = new ObjectMapper();
Sample sample=new Sample();
sample.setBar("val2");
sample.setFoo("val");
Map<String, Map<String, Object>> sampleData=new HashMap<>();
Map<String, Object> data_3=new HashMap<>();
Map<String, Object> data_4=new HashMap<>();
data_3.put("someInt", 1);
data_4.put("someBoolean", Boolean.TRUE);
sampleData.put("data_1", data_3);
sampleData.put("data_2", data_4);
sample.setData(sampleData);
try {
mapper.writeValue(new File("log.txt"), sample);
} catch (JsonGenerationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JsonMappingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
我认为这应该可以正常工作
您将需要一个自定义序列化程序,它遍历父级 Map
。对于每个嵌套的 Map
,您可以简单地使用 writeObjectField
和键,值作为字段名称和值。
您还需要自动序列化所有其他字段。您只能在一个字段上设置自定义序列化程序,但您仍然会在 JSON 中获得一个 data
字段。您实际上想要提升数据的内容,使它们看起来像 Sample
的字段,这需要 Sample
的自定义序列化程序。我能想到的自动序列化除 data
字段以外的所有其他内容的唯一方法是使用反射
以下序列化程序将产生 JSON,如您的问题:
class SampleSerializer extends StdSerializer<Sample> {
private static final List<Field> sampleFields;
public SampleSerializer() { this(null); }
private SampleSerializer(Class<Sample> t) { super(t); }
static {
sampleFields = Arrays.stream(Sample.class.getDeclaredFields())
.filter(f -> !("data".equals(f.getName())))
.collect(toList());
}
@Override
public void serialize(Sample sample, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeStartObject();
for (Field field : sampleFields) {
try {
field.setAccessible(true);
jgen.writeObjectField(field.getName(), field.get(sample));
} catch (IllegalAccessException ignored) {
}
}
for (Entry<String, Map<String, Object>> entry : sample.getData().entrySet()) {
jgen.writeObjectField(entry.getKey(), entry.getValue());
}
jgen.writeEndObject();
}
}
您还必须指定 SampleSerializer
将用于 Sample
,例如通过注释 class:
@JsonSerialize(using = SampleSerializer.class)
class Sample {
我想出了一个更简单的方法来做到这一点,而无需使用自定义序列化程序;它与@JsonAnyGetter 和@JsonAnySetter 一起使用。这是一个完整的例子。我正在粘贴关于此处粘贴示例的答案,因为它可能对其他人有用。
class Sample {
private String foo;
private String bar;
private Map<String, Map<String, Object>> data = new LinkedHashMap<>();
@JsonAnyGetter
public Map<String, Map<String, Object>> getData() {
return data;
}
@JsonAnySetter
public void set(String key, Map<String, Object>) {
data.put(key, object);
}
}