杰克逊+建筑师模式?

Gil*_*ili 80 java json jersey jackson

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

public Clinic(String name, Address address)
Run Code Online (Sandbox Code Playgroud)

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

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();
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

Rup*_*ott 127

只要您使用的是Jackson 2+,那么现在就可以为此提供支持.

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

@JsonDeserialize(builder = Address.Builder.class)
Run Code Online (Sandbox Code Playgroud)

然后,您需要将此注释添加到您的Builder类:

@JsonPOJOBuilder(buildMethodName = "create", withPrefix = "set")
Run Code Online (Sandbox Code Playgroud)

如果您乐意将Builder的create方法重命名为build,并且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();
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果希望一起删除`@JsonPOJOBuilder`注释,请将"create"重命名为"build",并使用`@JsonProperty`注释每个构建器设置器. (14认同)
  • @Randakar 我认为这并没有过时,因为 a) `@Jackonized` 是 Lombok 中刚刚发布的实验性功能。我认为不必要地鼓励采用实验性功能并不是一个好主意。b)问题没有提及或使用Lombok。我认为引入不必要的依赖来解决问题不是一个好主意。 (2认同)

vol*_*var 20

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

Builder(String city, String country) {...}
Run Code Online (Sandbox Code Playgroud)

然后你应该注释如下参数:

@JsonCreator
Builder(@JsonProperty("city")    String city, 
        @JsonProperty("country") String country) {...}
Run Code Online (Sandbox Code Playgroud)


Gil*_*ili 7

我最终使用@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();
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

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

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

我希望对您也有用。

  • 这现在已经过时了,在 Lombok 1.18.4 中,您可以使用“@Jacksonized”,它用一个东西替换内部构建器和杰克逊注释 (4认同)