杰克逊+建设者模式?


89

我希望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类?

Answers:


139

只要您使用的是Jackson 2+,现在就会对此提供内置支持

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

@JsonDeserialize(builder = Address.Builder.class)

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

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

如果您愿意重命名要构建的Builder的create方法,并且要以而不是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();
  }
}

14
如果希望一起删除@JsonPOJOBuilder所有注释,请将“ create”重命名为“ build”,并使用注释每个构建器设置器@JsonProperty
山姆·贝里

这是金色的。谢谢。
Mukul Goel

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

@Randakar我不认为这已经过时,因为a) @Jackonized 是Lombok中刚刚发布的实验功能。我认为不必要地鼓励采用实验性功能不是一个好主意。b)问题未提及或使用Lombok。我认为不必要引入依赖来解决问题不是一个好主意。
Rupert Madden-Abbott

19

@Rupert Madden-Abbott的答案起作用。但是,如果您使用的是非默认构造函数,例如

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

然后,应按如下所示注释参数:

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

9

在这种情况下适合我的解决方案(我使用了“ 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它用一个东西替换内部生成器和杰克逊注释
Randakar

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

这可能是您最终实现它的方式,但是此答案实际上并未回答所提出的问题。@Rupert Madden-Abbott发布的答案应标记为已接受。
kelnos

2

尽管很早以前就要求构建器模式,但目前尚不支持它(最后是Jira问题http://jira.codehaus.org/browse/JACKSON-469提交了)-可以添加一些东西如果有足够的需求,请下载1.8版(请确保在Jira上投票!)。这是一个合理的附加功能,仅受开发人员时间的延迟。但是我认为这将是很棒的补充。


2
Codehaus不再提供Jira,但此处描述了链接的问题:wiki.fasterxml.com/JacksonFeatureBuilderPattern
Paul,

自从添加了对Builder模式的支持以来,Jackson 2.2就已经支持了。
StaxMan

2

这对我有用:@NoArgsConstructor这样做的唯一缺点是可以再次执行= new ADTO()。但是,嘿,无论如何,我都不喜欢de code Police,告诉我如何使用某人的代码:-)因此,以您喜欢的方式使用POJO DTOS。有或没有生成器。我建议:与Builder合作,但请成为我的客人...

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

您不喜欢如何使用某人的代码?
masterxilo
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.