วิธีการยกเลิก / จัดลำดับวัตถุที่ไม่เปลี่ยนรูปโดยไม่มีตัวสร้างเริ่มต้นโดยใช้ ObjectMapper


107

ฉันต้องการทำให้เป็นอนุกรมและแยกส่วนของวัตถุที่ไม่เปลี่ยนสถานะโดยใช้ com.fasterxml.jackson.databind.ObjectMapper

คลาสที่ไม่เปลี่ยนรูปมีลักษณะดังนี้ (มีเพียง 3 คุณลักษณะภายในตัวรับและตัวสร้าง):

public final class ImportResultItemImpl implements ImportResultItem {

    private final ImportResultItemType resultType;

    private final String message;

    private final String name;

    public ImportResultItemImpl(String name, ImportResultItemType resultType, String message) {
        super();
        this.resultType = resultType;
        this.message = message;
        this.name = name;
    }

    public ImportResultItemImpl(String name, ImportResultItemType resultType) {
        super();
        this.resultType = resultType;
        this.name = name;
        this.message = null;
    }

    @Override
    public ImportResultItemType getResultType() {
        return this.resultType;
    }

    @Override
    public String getMessage() {
        return this.message;
    }

    @Override
    public String getName() {
        return this.name;
    }
}

อย่างไรก็ตามเมื่อฉันทำการทดสอบหน่วยนี้:

@Test
public void testObjectMapper() throws Exception {
    ImportResultItemImpl originalItem = new ImportResultItemImpl("Name1", ImportResultItemType.SUCCESS);
    String serialized = new ObjectMapper().writeValueAsString((ImportResultItemImpl) originalItem);
    System.out.println("serialized: " + serialized);

    //this line will throw exception
    ImportResultItemImpl deserialized = new ObjectMapper().readValue(serialized, ImportResultItemImpl.class);
}

ฉันได้รับข้อยกเว้นนี้:

com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class eu.ibacz.pdkd.core.service.importcommon.ImportResultItemImpl]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
 at [Source: {"resultType":"SUCCESS","message":null,"name":"Name1"}; line: 1, column: 2]
    at 
... nothing interesting here

ข้อยกเว้นนี้ขอให้ฉันสร้างตัวสร้างเริ่มต้น แต่นี่เป็นวัตถุที่ไม่เปลี่ยนรูปดังนั้นฉันจึงไม่ต้องการมี มันจะตั้งค่าคุณสมบัติภายในอย่างไร? มันจะทำให้ผู้ใช้ API สับสนโดยสิ้นเชิง

ดังนั้นคำถามของฉันคือฉันสามารถ de / serialize วัตถุที่ไม่เปลี่ยนรูปโดยไม่มีตัวสร้างเริ่มต้นได้หรือไม่?


ในขณะที่ desrializing desrializer ไม่ทราบเกี่ยวกับตัวสร้างใด ๆ ของคุณดังนั้นจึงเข้าสู่ตัวสร้างเริ่มต้น ด้วยเหตุนี้คุณจึงต้องสร้างตัวสร้างเริ่มต้นซึ่งจะไม่เปลี่ยนความไม่เปลี่ยนรูป นอกจากนี้ฉันยังเห็นว่าชั้นเรียนยังไม่สิ้นสุดเพราะเหตุใด มีใครสามารถแทนที่การทำงานได้หรือไม่
Abhinaba Basu

คลาสไม่สิ้นสุดเพราะฉันลืมเขียนไว้ที่นั่นขอบคุณที่สังเกตเห็น :)
คาล

คำตอบ:


151

เพื่อให้ Jackson รู้วิธีสร้างออบเจ็กต์สำหรับ deserialization ให้ใช้@JsonCreatorและ@JsonPropertyคำอธิบายประกอบสำหรับตัวสร้างของคุณดังนี้:

@JsonCreator
public ImportResultItemImpl(@JsonProperty("name") String name, 
        @JsonProperty("resultType") ImportResultItemType resultType, 
        @JsonProperty("message") String message) {
    super();
    this.resultType = resultType;
    this.message = message;
    this.name = name;
}

1
+1 ขอบคุณ Sergey มันใช้งานได้ .. แต่ฉันก็ประสบปัญหาแม้ว่าจะใช้คำอธิบายประกอบ JsonCreator แล้วก็ตาม .. แต่หลังจากการตรวจสอบบางอย่างฉันก็รู้ว่าฉันลืมใช้คำอธิบายประกอบ RequestBody ในทรัพยากรของฉัน ตอนนี้ได้ผล .. Thanx อีกครั้ง
Rahul

4
จะเกิดอะไรขึ้นถ้าคุณไม่สามารถเพิ่มตัวสร้างเริ่มต้นหรือคำอธิบายประกอบ@JsonCreatorและ@JsonPropertyคำอธิบายประกอบเนื่องจากคุณไม่มีสิทธิ์เข้าถึง POJO เพื่อเปลี่ยนแปลง ยังมีวิธีการ deserialize วัตถุหรือไม่?
Jack Straw

1
@JackStraw 1) คลาสย่อยจากออบเจ็กต์และแทนที่ getters และ setters ทั้งหมด 2) เขียน serializer / deserializer ของคุณเองสำหรับคลาสและใช้มัน (ค้นหา "custom serializer" 3) ห่อวัตถุและเขียน serializer / deserializer สำหรับคุณสมบัติเดียวเท่านั้น (อาจช่วยให้คุณทำงานน้อยกว่าการเขียน serializer สำหรับ ชั้นเรียนเอง แต่อาจจะไม่ใช่)
ErikE

+1 ความคิดเห็นของคุณช่วยฉันไว้! วิธีแก้ปัญหาทั้งหมดที่ฉันพบคือสร้างตัวสร้างเริ่มต้น ...
Nilson Aguiar

34

คุณสามารถใช้คอนสตรัคเตอร์เริ่มต้นส่วนตัวจากนั้นแจ็คสันจะเติมฟิลด์ผ่านการสะท้อนแม้ว่าจะเป็นขั้นสุดท้ายส่วนตัวก็ตาม

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


เพียงเพื่อสร้างคำตอบนี้หากคลาสที่คุณต้องการ deserialise ขยายคลาสอื่นคุณสามารถกำหนดคอนสตรัคเตอร์เริ่มต้นของซุปเปอร์คลาส (หรือทั้งสองคลาส) ได้รับการป้องกัน
KWILLIAMS

4

คำตอบแรกของ Sergei Petunin ถูกต้อง อย่างไรก็ตามเราสามารถลดความซับซ้อนของโค้ดด้วยการลบคำอธิบายประกอบ @JsonProperty ที่ซ้ำซ้อนในแต่ละพารามิเตอร์ของตัวสร้าง

สามารถทำได้โดยเพิ่ม com.fasterxml.jackson.module.paramnames.ParameterNamesModule ใน ObjectMapper:

new ObjectMapper()
        .registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES))

(Btw: โมดูลนี้ถูกลงทะเบียนโดยค่าเริ่มต้นใน SpringBoot หากคุณใช้ ObjectMapper bean จาก JacksonObjectMapperConfiguration หรือถ้าคุณสร้าง ObjectMapper ของคุณเองด้วย bean Jackson2ObjectMapperBuilder คุณสามารถข้ามการลงทะเบียนโมดูลด้วยตนเองได้)

ตัวอย่างเช่น:

public class FieldValidationError {
  private final String field;
  private final String error;

  @JsonCreator
  public FieldValidationError(String field,
                              String error) {
    this.field = field;
    this.error = error;
  }

  public String getField() {
    return field;
  }

  public String getError() {
    return error;
  }
}

และ ObjectMapper deserializes json นี้โดยไม่มีข้อผิดพลาดใด ๆ :

{
  "field": "email",
  "error": "some text"
}

คำตอบที่ยอดเยี่ยมใช้งานได้ดี
Ali
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.