【问题标题】:Jackson Custom serializer for custom behaviour of some fields while having default behaviour for rest of the fieldsJackson 自定义序列化程序,用于某些字段的自定义行为,同时对其余字段具有默认行为
【发布时间】:2017-05-23 15:35:25
【问题描述】:

例如,我有一个 POJO 定义如下,带有 jackson-corejackson-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_1data_2是主Map的键,它们的内部属性由它们的子映射(嵌套映射)组成。此外,实际的属性数据根本不应该出现在结果 JSON 中。

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

【问题讨论】:

    标签: java json serialization jackson fasterxml


    【解决方案1】:

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

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

    我认为这应该可以正常工作

    【讨论】:

    • 对不起,我不能为此使用静态方法,pojo 与 spring 挂钩,并通过 spring-data 进入 NoSql 存储。
    【解决方案2】:

    为此,您需要一个自定义序列化程序,它迭代父级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,例如通过注释类:

    @JsonSerialize(using = SampleSerializer.class)
    class Sample {
    

    【讨论】:

    • 这就是我想要的。但是,jgen.writeStringField("foo", sample.getFoo()); jgen.writeStringField("bar", sample.getBar()); 这两个语句是不可取的,因为这将是一个包含许多其他复杂字段的非常复杂的 pojo。我想对杰克逊说点什么,根据注释序列化 foo 和 bar 字段,让我指定数据字段的行为
    • 我已经修改为使用反射来自动序列化其他所有内容并手动序列化数据。它有效,但看起来并不漂亮。
    【解决方案3】:

    我想出了一种不使用自定义序列化程序的更简单的方法;它与@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);
         }
    }
    

    Example

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-09
      • 2013-11-15
      • 2016-02-12
      • 2013-03-01
      • 1970-01-01
      • 2014-09-16
      相关资源
      最近更新 更多