Java Jackson自定义序列化程序用于某些字段的自定义行为,而对其余字段具有默认行为

Java Jackson自定义序列化程序用于某些字段的自定义行为,而对其余字段具有默认行为,java,json,serialization,jackson,fasterxml,Java,Json,Serialization,Jackson,Fasterxml,例如,我有一个定义如下的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; } 这里的data_1和data_2是主Map的键,它们的内部属性由它们

例如,我有一个定义如下的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;
}
这里的
data_1
data_2
是主
Map
的键,它们的内部属性由它们的子映射(嵌套映射)组成。此外,实际的属性数据不应该出现在生成的JSON中


请注意,foo和bar是字段的示例,实际上pojo有15个以上的字段。

请更正您的类变量数据, 应该是这样的

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();
    }



}
publicstaticvoid序列化示例(){
ObjectMapper mapper=新的ObjectMapper();
样本=新样本();
样本。立根节(“val2”);
样本:setFoo(“val”);
Map sampleData=new HashMap();
映射数据_3=新HashMap();
映射数据_4=新HashMap();
数据_3.put(“someInt”,1);
数据4.put(“someBoolean”,Boolean.TRUE);
sampleData.put(“数据1”,数据3);
sampleData.put(“数据2”,数据4);
sample.setData(sampleData);
试一试{
writeValue(新文件(“log.txt”),示例);
}捕获(JsonGenerationException e){
//TODO自动生成的捕捉块
e、 printStackTrace();
}捕获(JsonMappingException e){
//TODO自动生成的捕捉块
e、 printStackTrace();
}捕获(IOE异常){
//TODO自动生成的捕捉块
e、 printStackTrace();
}
}

这应该很好,我想

请更正您的类变量数据, 应该是这样的

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();
    }



}
publicstaticvoid序列化示例(){
ObjectMapper mapper=新的ObjectMapper();
样本=新样本();
样本。立根节(“val2”);
样本:setFoo(“val”);
Map sampleData=new HashMap();
映射数据_3=新HashMap();
映射数据_4=新HashMap();
数据_3.put(“someInt”,1);
数据4.put(“someBoolean”,Boolean.TRUE);
sampleData.put(“数据1”,数据3);
sampleData.put(“数据2”,数据4);
sample.setData(sampleData);
试一试{
writeValue(新文件(“log.txt”),示例);
}捕获(JsonGenerationException e){
//TODO自动生成的捕捉块
e、 printStackTrace();
}捕获(JsonMappingException e){
//TODO自动生成的捕捉块
e、 printStackTrace();
}捕获(IOE异常){
//TODO自动生成的捕捉块
e、 printStackTrace();
}
}

这应该可以很好地工作,我想你需要一个自定义的序列化程序,它可以在父映射上迭代。对于每个嵌套的
映射
,您只需使用
writeObjectField
键、值作为字段名和值

您还需要自动序列化所有其他字段。您可以仅在字段上设置自定义序列化程序,但仍然会在JSON中获得一个
data
字段。实际上,您希望提升数据的内容,使其看起来像
Sample
的字段,这需要为
Sample
定制序列化程序。我能想到的自动序列化除
数据
字段之外的所有其他内容的唯一方法是使用反射

以下序列化程序将生成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();
    }
}

为此,您需要一个自定义序列化程序,该序列化程序在父
Map
上进行迭代。对于每个嵌套的
映射
,您只需使用
writeObjectField
键、值作为字段名和值

您还需要自动序列化所有其他字段。您可以仅在字段上设置自定义序列化程序,但仍然会在JSON中获得一个
data
字段。实际上,您希望提升数据的内容,使其看起来像
Sample
的字段,这需要为
Sample
定制序列化程序。我能想到的自动序列化除
数据
字段之外的所有其他内容的唯一方法是使用反射

以下序列化程序将生成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();
    }
}

我想出了一个更简单的方法,不用使用自定义序列化程序;这是与@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);
     }
}
类示例{
私人字符串foo;
私人弦杆;
私有映射数据=新LinkedHashMap();
@JsonAnyGetter
公共地图getData(){
返回数据;
}
@JSONANYSETER
公共无效集(字符串键、映射){
数据输入(键、对象);
}
}

我想出了一种更简单的方法,不用使用自定义序列化程序;这是与@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);
     }
}
类示例{
私人字符串foo;
私人弦杆;
私有映射数据=新LinkedHashMap();
@JsonAnyGetter
公共地图getData(){
返回数据;
}
@JSONANYSETER
公共无效集(字符串键、映射){
数据输入(键、对象);
}
}

这就是我想要的。但是,这两条语句
jgen.writeStringField(“foo”,sample.getFoo());writeStringField(“bar”,sample.getBar())是不可取的,因为它将是一个包含许多其他复杂字段的非常复杂的pojo。我想以某种方式告诉jackson,根据注释序列化foo和bar字段,并让我指定数据字段的行为,我已经修改为使用反射自动序列化所有其他内容并手动序列化数据。它能用,但看起来不漂亮