杰克逊序列化:包装字段

4

有一个已知的好的案例,当我们展开嵌套对象并将其字段写入主对象时,我需要做一个反向任务。

我有一个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的结构,因此我看到了三种可能的方法:
  1. 编写自定义序列化程序。但是我认为编写这样的序列化程序需要更多的工作量,比手动序列化对象还要困难。
  2. 创建代理Java对象,反映JSON的正确结构,并对此类代理进行序列化。
  3. 在生成JSON后修改它。(我担心这会对JSON的重新读取和重写造成很大的开销)。
有人制作过这样的序列化程序或者知道其他选项来生成我需要的结构的JSON吗?
对于“自定义序列化程序”,我想重用标准的BeanSerializer,我不想手动写出所有字段:
  1. 隐藏注释字段。
  2. 写出bean,不包括注释字段,但不关闭对象。(不调用jgen.writeEndObject();)
  3. 写出包装字段。
  4. 关闭对象。

sgrillon的解决方案适合您吗? - Stéphane GRILLON
3个回答

0

你需要更改你的模型。

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

在主程序中运行:

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":"..."}}

但这正是我不想做的。它会在应用程序的其他层面上造成困难。 - dkiselev
我更改了解决方案。通常,这个解决方案正是你所需要的。 - Stéphane GRILLON

0

如果不改变您的模型就想获得该功能,请考虑编写自定义序列化程序来完成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();
        }
    }
}

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

抱歉,您在我发布此回答时编辑了您的问题,此答案使用原始字段,但我认为您可以理解。 - IanGabes
1
当然,我明白这个想法,但是我想使用BeanSerializer,我不想手动编写所有字段。 - dkiselev

0

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

当然,如果你正在从类似的JSON结构创建Java对象,你可能也需要创建一个自定义反序列化器。

记住,如果你发现许多对象共享相似的结构,你总是可以使用反射来创建一个“通用”的序列化器。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接