@JsonUnwrapped for Custom Serializer

时间:2014-11-16 17:22:44

标签: java json jackson

我有一个相对复杂的对象,其中包含许多字段。我需要使用自定义序列化程序序列化其中一个字段,但需要模拟@JsonUnwrapped功能。

为了简单起见,我将其分为两个领域:

public class MyClass
{
  @JsonProperty("subject")
  private final String subject;
  @JsonSerialize(using=MySenderSerializer.class)
  private final MailActor sender;
}

我的自定义序列化程序类如下:

public class MySenderSerializer extends StdSerializer<MailActor>
{
  public MySenderSerializer()
  {
    super(MailActor.class, true);
  }

  @Override
  public void serialize(final MailActor value, final JsonGenerator gen, final SerializerProvider provider) throws IOException
  {
    gen.writeStringField("from_name", value.getName());
    gen.writeStringField("from_email", value.getAddress());
  }
}

所有这些都很好,除了输出JSON看起来像这样:

{
  ...
  "subject": "test subject",
  "sender": {
    "from_name": "test from",
    "from_email": "test@test.com"
  },
  ...
}

我需要打开sender字段,以便JSON看起来像这样:

{
  ...
  "subject": "test subject",
  "from_name": "test from",
  "from_email": "test@test.com",
  ...
}

如果我使用的是标准序列化程序,我可以使用@JsonUnwrapped注释来执行此操作,但它似乎与自定义序列化程序不兼容。如何在不为MyClass对象编写自定义序列化程序的情况下获取所需的JSON输出?

2 个答案:

答案 0 :(得分:1)

我也不得不寻找 @JsonUnwrapped 的替代品,因为这引起了我对这个问题的一些无关问题。

我实施的解决方案同样适用于您的情况,使用 @JsonAnyGetter 。在MyClass中忽略需要特殊序列化的属性,而是将其添加到 JsonAnyGetter

public class MyClass
{
    @JsonProperty("subject")
    private final String subject;
    @JsonIgnore
    private final MailActor sender;

    @JsonAnyGetter
    public Map<String, Object> serializeActor() {
         return sender.serializeToMap();
         // Of course, here you could create an empty map
         // and add the properties of many different classes.
    }
}

然后在MailActor类中实现该方法,该方法将返回具有您可能需要的属性的地图。

public Map<String, Object> serializeToMap() {
    final Map<String, Object> properties = new ArrayMap<>();
    properties.put("from_name", this.getName());
    properties.put("from_email", this.getAddress());
    return properties;
}

这样做的问题是反序列化对象,因为@JsonAnySetter没有在地图中获得带有JSON的地图,就像你用上面的例子一样。在你的情况下,它更难,因为你的班级&#39;属性是最终的。您需要使用必须创建所有属性的@JsonCreator。有点像:

@JsonCreator
public MyClass( Map< String, String > json ) {
    if (json.containsKey("subject")) {
        subject = json.get("subject");
    } else {
        subject = "";
    }

    String name, address;
    if (json.containsKey("from_name")) {
        name = json.get("from_name");
    } else {
        name = "";
    }
    if (json.containsKey("from_email")) {
        address = json.get("from_email");
    } else {
        address = "";
    }
    sender = new MailActor(name, address);
}

自从您发布问题以来已经有一段时间了,希望这对某些寻找替代方案的人有所帮助。

答案 1 :(得分:0)

嗯,那是因为你设计它是这样的,当杰克逊映射一个对象时,它会将内部属性映射为该对象的子属性,如果你希望它将这两个字段序列化,就好像它们是成员一样MyClass而不是MailActor,然后将它们声明为。

这可能会指出您的对象设计可能存在一些小缺陷。

我会为MyClass对象编写一个客户序列化程序,但从长远来看仍然不是一个可行的解决方案。