【问题标题】:@JsonUnwrapped for Custom Serializer@JsonUnwrapped 用于自定义序列化程序
【发布时间】:2014-11-16 17:22:43
【问题描述】:

我有一个相对复杂的对象,其中包含许多字段。我需要使用自定义序列化程序序列化其中一个字段,但需要模拟@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 输出?

【问题讨论】:

    标签: java json jackson


    【解决方案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 的映射。在您的情况下,这更加困难,因为您的类的属性是最终的。您需要使用必须创建所有属性的@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);
    }
    

    您发布问题已经有一段时间了,希望这在某种程度上对寻找替代方案的人有所帮助。

    【讨论】:

      【解决方案2】:

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

      这可能表明您的对象设计可能存在一些小缺陷。

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

      【讨论】:

      • 我不会说对象设计有缺陷;它在其他领域运行良好,包括同一 API 中的其他调用,问题在于它适合特定 API 中特定调用的预期格式。
      • 那么它应该是一个不同的对象,我想。因为您不想要一个内部带有 Sender 对象的 Mail 对象,所以您想要一个带有 sender 属性的 Mail 对象,我并不认为这是一个 json 解析问题,这就是我所说的对象设计。
      • 感谢您的持续回复,但我与杰克逊的斗争不是对象模型。相同的信息(姓名和电子邮件地址)以不同的格式显示在 API 的不同部分:有时包装有时不包装,并且具有不同的字段名称。无论我选择什么作为我的对象设计,我都会遇到这个问题,这个问题通常在 Jackson 中用@JsonUnwrapped 解决,但这与自定义序列化程序不兼容。因此,问题是我是否可以使用自定义序列化程序做一些等效的事情。
      猜你喜欢
      • 2021-04-02
      • 1970-01-01
      • 2016-07-17
      • 2012-10-26
      • 2020-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-29
      相关资源
      最近更新 更多