【问题标题】:Jackson serialization: Wrap fieldsJackson 序列化:包装字段
【发布时间】:2015-09-30 19:28:57
【问题描述】:

当我们打开嵌套对象并将其字段写入主对象时,有一个众所周知的案例,我需要做一个逆向任务。

我有一个 POJO:

class A {
    private String id = "id1";

    @JsonWrap("properties")
    private String property1 = "...";

    @JsonWrap("properties")
    private String property2 = "...";

    // getters and setters
}

默认序列化程序将按预期生成

{
    "id": "id1",
    "property1": "...",    
    "property2": "..."    
}

但是,我的 JSON 应该符合一些规范,为此,我需要将 property1property2 包装在 properties 包装器中。所以结果应该是这样的:

{
    "id": "id1",
    "properties": 
    {
        "property1": "...",
        "property2": "..."
    }
}

我不想改变 POJO 的结构,所以我看到了 3 种可能的方法:

  1. 编写自定义序列化程序。但在我看来,编写这样的序列化程序比手动序列化对象需要更多的努力。
  2. 创建将反映 JSON 正确结构的代理 Java 对象,并序列化此类代理。
  3. 在生成 JSON 后对其进行修改。 (恐怕重读和重写 JSON 的开销会很大)。

有没有人制作这样的序列化器,或者可能知道其他选项来生成具有我需要的结构的 JSON?

对于custom serializer,我想重用标准的BeanSerializer,不想手动写出所有字段:

  1. 隐藏带注释的字段。
  2. 写出 bean,不带注释字段,但不要关闭对象。 (请勿拨打jgen.writeEndObject();
  3. 写出包装的字段。
  4. 关闭对象。

【问题讨论】:

  • sgrillon 解决方案适合您吗?

标签: java json jackson


【解决方案1】:

要在不更改模型的情况下获得该功能,请查看编写自定义序列化程序以完成 Jackson 本身无法解决的问题。我们在模型类A上注解了使用定义好的序列化器的具体说明,然后使用JsonGenerator来具体定义我们要的结构。

@JsonSerialize(using = ASerializer.class)
class A {
    private String field1;
    private String innerField1;
    private String innerField2;
    // getters and setters


    public static class ASerializer extends JsonSerializer<A> {
        @Override
        public void serialize(A value, JsonGenerator jgen, SerializerProvider provider) 
          throws IOException, JsonProcessingException {
            jgen.writeStartObject();
                jgen.writeStringField("field1", value.getField1());
                jgen.writeObjectFieldStart("wrapper");
                    jgen.writeStringField("innerField1", value.getInnerField1());
                    jgen.writeStringField("innerField2", value.getInnerField2());
                jgen.writeEndObject();
            jgen.writeEndObject();
        }
    }
}

在这种情况下,我使用了静态内部类,但您可以根据可见性将 Serializer 放置在最适合您的项目结构的位置。对于一次性的特殊情况序列化程序,这是我倾向于做的。

【讨论】:

  • 抱歉,您在我发布此问题时编辑了您的问题,此答案使用原始字段,但我认为您可以理解。
  • 是的,我当然有这个想法,但我想使用 BeanSerializer,我不想手动编写所有字段。
【解决方案2】:

听起来你需要创建一个自定义序列化程序:http://wiki.fasterxml.com/JacksonHowToCustomSerializers

当然,如果您从类似的 JSON 结构创建 Java 对象,您可能还需要创建自定义反序列化器。

请记住,如果您发现许多对象共享相似的结构,则始终可以使用反射来创建“通用”序列化程序。

【讨论】:

    【解决方案3】:

    你需要改变你的模型。

    @JsonSerialize(using = ASerializer.class)
    class A {
        private String id;
        private String property1;
        private String property2;
    
        // getters and setters
    
        public static class ASerializer extends JsonSerializer<A> {
            @Override
            public void serialize(A value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
                jgen.writeStartObject();
                jgen.writeStringField("id", value.getId());
                jgen.writeObjectFieldStart("properties");
                jgen.writeStringField("property1", value.getProperty1());
                jgen.writeStringField("property2", value.getProperty2());
                jgen.writeEndObject();
                jgen.writeEndObject();
            }
        }
    }
    

    在 main 中运行:

    A a = new A();
    a.setId("id1");
    a.setProperty1("...");
    a.setProperty2("...");
    ObjectMapper mapper = new ObjectMapper();
    ObjectWriter writer = mapper.writer();
    String json = writer.writeValueAsString(a);
    System.out.println(json);
    

    输出:

    {"id":"id1","properties":{"property1":"...","property2":"..."}}
    

    【讨论】:

    • 但这正是我不想做的。这会给应用程序的其他层带来困难。
    • 我改变了解决方案。通常,这个解决方案就是你所需要的。
    猜你喜欢
    • 2014-10-06
    • 2023-01-26
    • 2012-08-31
    • 2015-02-26
    • 2021-06-23
    • 2012-05-01
    • 2011-07-30
    • 1970-01-01
    • 2012-07-04
    相关资源
    最近更新 更多