แบบแจ็คสัน + บิลเดอร์?


89

ฉันต้องการให้แจ็คสันยกเลิกการกำหนดค่าคลาสด้วยตัวสร้างต่อไปนี้:

public Clinic(String name, Address 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 เพื่อสร้าง Address ด้วยตัวเองหรือไม่? หรือมีวิธีทำให้ Jackson ใช้คลาส Builder เองหรือไม่?

คำตอบ:


142

ตราบใดที่คุณใช้ Jackson 2+ ตอนนี้ก็มีการรองรับสิ่งนี้ในตัวแล้ว

ก่อนอื่นคุณต้องเพิ่มคำอธิบายประกอบนี้ในAddressชั้นเรียนของคุณ:

@JsonDeserialize(builder = Address.Builder.class)

จากนั้นคุณต้องเพิ่มคำอธิบายประกอบนี้ในBuilderชั้นเรียนของคุณ:

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

คุณสามารถข้ามคำอธิบายประกอบที่สองนี้ได้หากคุณพอใจที่จะเปลี่ยนชื่อวิธีการสร้างของ Builder ในการสร้างและตัวตั้งค่าของ Builder ของคุณจะนำหน้าด้วยแทนการตั้งค่า

ตัวอย่างเต็ม:

@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คำอธิบายประกอบทั้งหมดเข้าด้วยกันเปลี่ยนชื่อ "สร้าง" เพื่อ "สร้าง" และอธิบายแต่ละ setters @JsonPropertyสร้างด้วย
Sam Berry

นี่คือสีทอง ขอบคุณ.
Mukul Goel

ตอนนี้ล้าสมัยแล้วด้วย Lombok 1.18.4 คุณสามารถใช้@Jacksonizedซึ่งแทนที่ตัวสร้างด้านในและคำอธิบายประกอบของแจ็คสันด้วยสิ่งเดียว
Randakar

@Randakar ฉันไม่คิดว่านี่จะล้าสมัยเพราะ a) @Jackonized เป็นฟีเจอร์ทดลองที่เพิ่งเปิดตัวในลอมบอก ฉันไม่คิดว่าเป็นความคิดที่ดีที่จะส่งเสริมให้ใช้คุณลักษณะทดลองโดยไม่จำเป็น b) คำถามไม่ได้กล่าวถึงหรือใช้ลอมบอก ฉันไม่คิดว่าเป็นความคิดที่ดีที่จะแนะนำการพึ่งพาเพื่อแก้ปัญหาโดยไม่จำเป็น
Rupert Madden-Abbott

20

คำตอบจาก @Rupert Madden-Abbott ได้ผล อย่างไรก็ตามหากคุณมีตัวสร้างที่ไม่ใช่ค่าเริ่มต้นเช่น

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

จากนั้นคุณควรใส่คำอธิบายประกอบพารามิเตอร์ดังต่อไปนี้:

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

9

วิธีแก้ปัญหาที่เหมาะสำหรับฉันในกรณีนี้ (ฉันใช้คำอธิบายประกอบตัวสร้าง "ลอมบอก")

@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 ข้อเสียเปรียบเพียงประการเดียวคือสามารถทำ = ADTO ใหม่ () ได้อีกครั้ง แต่เดี๋ยวก่อนฉันไม่ชอบตำรวจรหัส แต่อย่างใดบอกวิธีใช้รหัส someones :-) ดังนั้นใช้ POJO DTOS ในแบบที่คุณชอบ มีหรือไม่มีตัวสร้าง ฉันแนะนำ: ทำกับ Builder แต่เป็นแขกของฉัน ...

@Data
@Builder
//Dont forget this! Otherwise no Jackson serialisation possible!
@NoArgsConstructor
@AllArgsConstructor
public class ADTO {
.....
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.