【问题标题】:Jackson + Builder Pattern?杰克逊 + 建设者模式?
【发布时间】:2011-06-26 07:45:42
【问题描述】:

我希望 Jackson 使用以下构造函数反序列化一个类:

public Clinic(String name, Address address)

反序列化第一个参数很容易。问题是地址被定义为:

public class Address {
  private Address(Map<LocationType, String> components)
  ...

  public static class Builder {
    public Builder setCity(String value);
    public Builder setCountry(String value);
    public Address create();
  }
}

并且构造如下:new Address.Builder().setCity("foo").setCountry("bar").create();

有没有办法从 Jackson 获取键值对以便自己构建地址?或者,有没有办法让 Jackson 使用 Builder 类本身?

【问题讨论】:

    标签: java json jersey jackson


    【解决方案1】:

    只要你使用的是 Jackson 2+,那么现在就有built in support for this

    首先您需要将此注释添加到您的Address 类中:

    @JsonDeserialize(builder = Address.Builder.class)
    

    那么你需要把这个注解添加到你的Builder类中:

    @JsonPOJOBuilder(buildMethodName = "create", withPrefix = "set")
    

    如果您愿意重命名要构建的 Builder 的 create 方法,并且您的 Builder 的 setter 的前缀是 with,而不是 set,则可以跳过第二个注释。

    完整示例:

    @JsonDeserialize(builder = Address.Builder.class)
    public class Address
    {
      private Address(Map<LocationType, String> components)
      ...
    
      @JsonPOJOBuilder(buildMethodName = "create", withPrefix = "set")
      public static class Builder
      {
        public Builder setCity(String value);
        public Builder setCountry(String value);
        public Address create();
      }
    }
    

    【讨论】:

    • 如果希望同时删除 @JsonPOJOBuilder 注释,请将“create”重命名为“build”并使用 @JsonProperty 注释每个构建器设置器。
    • 这是黄金。谢谢。
    • 现在已经过时了,在 Lombok 1.18.4 中,您可以使用 @Jacksonized 将内部构建器和 jackson 注释替换为一个东西
    • @Randakar 我认为这并不过时,因为 a) @Jackonized 是 Lombok 中刚刚发布的实验性功能。我认为不必要地鼓励采用实验性功能不是一个好主意。 b)问题没有提到或使用龙目岛。我认为不必要地引入依赖项来解决问题不是一个好主意。
    • 很公平。我正在用龙目岛相关的信息更新另一个答案,显然我撒得太宽了。
    【解决方案2】:

    @Rupert Madden-Abbott 的回答很有效。但是,如果您有一个非默认构造函数,例如,

    Builder(String city, String country) {...}
    

    那么你应该如下注释参数:

    @JsonCreator
    Builder(@JsonProperty("city")    String city, 
            @JsonProperty("country") String country) {...}
    

    【讨论】:

      【解决方案3】:

      在这种情况下适合我的解决方案(我使用了“Lombok”构建器注释)。

      @Getter
      @Builder(builderMethodName = "builder")
      @NoArgsConstructor(access = AccessLevel.PRIVATE)
      @AllArgsConstructor(access = AccessLevel.PRIVATE)
      @JsonAutoDetect(
          fieldVisibility = JsonAutoDetect.Visibility.ANY,
          creatorVisibility = JsonAutoDetect.Visibility.ANY
      )
      

      希望对你也有用。

      【讨论】:

      • 现在已经过时了,在 Lombok 1.18.4 中,您可以使用 @Jacksonized 将内部构建器和 jackson 注释替换为一个东西
      【解决方案4】:

      我最终使用@JsonDeserialize 实现了这一点,如下所示:

      @JsonDeserialize(using = JacksonDeserializer.class)
      public class Address
      {...}
      
      @JsonCachable
      static class JacksonDeserializer extends JsonDeserializer<Address>
      {
          @Override
          public Address deserialize(JsonParser parser, DeserializationContext context)
              throws IOException, JsonProcessingException
          {
              JsonToken token = parser.getCurrentToken();
              if (token != JsonToken.START_OBJECT)
              {
                  throw new JsonMappingException("Expected START_OBJECT: " + token, parser.getCurrentLocation());
              }
              token = parser.nextToken();
              Builder result = new Builder();
              while (token != JsonToken.END_OBJECT)
              {
                  if (token != JsonToken.FIELD_NAME)
                  {
                      throw new JsonMappingException("Expected FIELD_NAME: " + token, parser.getCurrentLocation());
                  }
                  LocationType key = LocationType.valueOf(parser.getText());
      
                  token = parser.nextToken();
                  if (token != JsonToken.VALUE_STRING)
                  {
                      throw new JsonMappingException("Expected VALUE_STRING: " + token, parser.getCurrentLocation());
                  }
                  String value = parser.getText();
      
                  // Our Builder allows passing key-value pairs
                  // alongside the normal setter methods.
                  result.put(key, value);
                  token = parser.nextToken();
              }
              return result.create();
          }
      }
      

      【讨论】:

      • 这可能是你最终实现它的方式,但这个答案实际上并没有回答所提出的问题。 @Rupert Madden-Abbott 发布的答案应标记为已接受。
      【解决方案5】:

      目前尚不支持构建器模式,尽管它已经在很久以前提出请求(最后 Jira 问题 http://jira.codehaus.org/browse/JACKSON-469 已提交)——如果有足够的需求,它可能会在 1.8 版本中添加(确保在 Jira 投票!)。这是一个合理的附加功能,只是延迟了开发人员的时间。但我认为这将是一个很好的补充。

      【讨论】:

      【解决方案6】:

      这对我有用:@NoArgsConstructor 唯一的缺点是可以再次执行 = new ADTO() 。 但是,嘿,无论如何我都不喜欢解码警察,告诉我如何使用某人的代码:-) 所以,按照你喜欢的方式使用我的 POJO DTOS。有或没有建造者。我建议:和 Builder 一起做,但做我的客人......

      @Data
      @Builder
      //Dont forget this! Otherwise no Jackson serialisation possible!
      @NoArgsConstructor
      @AllArgsConstructor
      public class ADTO {
      .....
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-01-08
        • 1970-01-01
        • 1970-01-01
        • 2015-12-05
        • 1970-01-01
        相关资源
        最近更新 更多