JsonMappingException: Không tìm thấy hàm tạo phù hợp cho loại [kiểu đơn giản, lớp]: không thể khởi tạo từ đối tượng JSON


437

Tôi gặp lỗi sau khi cố gắng nhận yêu cầu JSON và xử lý nó:

org.codehaus.jackson.map.JsonMappingException: Không tìm thấy hàm tạo phù hợp nào cho loại [loại đơn giản, lớp com.myweb.ApplesDO]: không thể khởi tạo từ đối tượng JSON (cần thêm / bật thông tin loại?)

Đây là JSON tôi đang cố gắng gửi:

{
  "applesDO" : [
    {
      "apple" : "Green Apple"
    },
    {
      "apple" : "Red Apple"
    }
  ]
}

Trong Trình điều khiển, tôi có chữ ký phương thức sau:

@RequestMapping("showApples.do")
public String getApples(@RequestBody final AllApplesDO applesRequest){
    // Method Code
}

AllApplesDO là một trình bao bọc của ApplesDO:

public class AllApplesDO {

    private List<ApplesDO> applesDO;

    public List<ApplesDO> getApplesDO() {
        return applesDO;
    }

    public void setApplesDO(List<ApplesDO> applesDO) {
        this.applesDO = applesDO;
    }
}

TáoDO:

public class ApplesDO {

    private String apple;

    public String getApple() {
        return apple;
    }

    public void setApple(String appl) {
        this.apple = apple;
    }

    public ApplesDO(CustomType custom){
        //constructor Code
    }
}

Tôi nghĩ rằng Jackson không thể chuyển đổi JSON thành các đối tượng Java cho các lớp con. Vui lòng trợ giúp với các tham số cấu hình để Jackson chuyển đổi JSON thành Đối tượng Java. Tôi đang sử dụng Spring Framework.

EDIT: Bao gồm lỗi lớn gây ra vấn đề này trong lớp mẫu ở trên - Vui lòng xem câu trả lời được chấp nhận cho giải pháp.


2
Tôi không thấy bất kỳ lớp con nào trong đoạn mã trên, đây có phải là mã bạn đang cố gắng hoặc bạn đang tạo ra một ví dụ đơn giản hơn?
gkamal

Tôi đã thêm một câu trả lời với một số giải thích thêm về cách thức hoạt động. Về cơ bản, bạn cần nhận ra Java không giữ các tên đối số phương thức trong thời gian chạy.
Vlasec

Câu trả lời:


565

Vì vậy, cuối cùng tôi nhận ra vấn đề là gì. Đây không phải là vấn đề cấu hình của Jackson như tôi nghi ngờ.

Trên thực tế, vấn đề là ở Lớp ApplesDO :

public class ApplesDO {

    private String apple;

    public String getApple() {
        return apple;
    }

    public void setApple(String apple) {
        this.apple = apple;
    }

    public ApplesDO(CustomType custom) {
        //constructor Code
    }
}

Có một hàm tạo tùy chỉnh được định nghĩa cho lớp làm cho nó trở thành hàm tạo mặc định. Giới thiệu một công cụ xây dựng giả đã khiến lỗi biến mất:

public class ApplesDO {

    private String apple;

    public String getApple() {
        return apple;
    }

    public void setApple(String apple) {
        this.apple = apple;
    }

    public ApplesDO(CustomType custom) {
        //constructor Code
    }

    //Introducing the dummy constructor
    public ApplesDO() {
    }

}

Tôi có thể hỏi CustomType đến từ đâu không. Tôi đang thử một cấu trúc như thế này nhưng tôi hoàn toàn mới với Java.
andho

181
Bạn có thể sử dụng jackson với các lớp bên trong (lồng nhau), tuần tự hóa hoạt động tốt trong trường hợp này. Điều vấp ngã duy nhất là lớp bên trong phải được đánh dấu là "tĩnh" để khử lưu huỳnh hoạt động đúng. Xem exlanation
jpennell

3
Ai đó có thể vui lòng giải thích tại sao điều này xảy ra? Tôi đã có một lỗi rất giống nhau. Nghĩ rằng tất cả các nhà xây dựng đúng đã được đưa ra, nhưng không thể giải trừ. Nó chỉ hoạt động sau khi tôi thêm một hàm tạo giả, sau khi đọc bài viết này.
dùng8658912

6
@Suman Tôi sẽ không gọi đây là một hàm tạo giả - nó chỉ là hàm tạo mặc định. Nó không chỉ hoàn toàn hợp lệ, mà còn được yêu cầu cho nhiều loại xử lý kiểu java bean. (Và, tất nhiên, nó làm tôi vấp ngã. :-))
crazy4jesus

2
Nếu bạn không muốn thêm hàm tạo mặc định (ví dụ: khi bạn đang xử lý các đối tượng không thay đổi). Bạn sẽ phải cho biết nhà xây dựng hoặc phương thức nhà máy nào sẽ sử dụng để khởi tạo đối tượng bằng cách sử dụng chú thích JsonCreator.
Raul

375

Điều này xảy ra vì những lý do sau:

  1. lớp bên trong của bạn nên được định nghĩa là tĩnh

    private static class Condition {  //jackson specific    
    }
  2. Có thể là bạn không có hàm tạo mặc định trong lớp ( CẬP NHẬT: Điều này dường như không phải là trường hợp)

    private static class Condition {
        private Long id;
    
        public Condition() {
        }
    
        // Setters and Getters
    }
  3. Nó có thể là Setters của bạn không được xác định đúng hoặc không hiển thị (ví dụ: setter private)


95
lớp tĩnh tạo ra sự khác biệt trong trường hợp của tôi. Cảm ơn!
jalogar

3
Và tất nhiên, bạn không cần phải khai báo một hàm tạo mặc định không có đối số, Java sẽ làm điều đó cho bạn ! (Miễn là bạn không định nghĩa bất kỳ nhà xây dựng nào khác.)
Jonik

1
@Jonik, đúng rồi! Câu trả lời của tôi đã cũ, nếu tôi nhớ chính xác, vì Jackson đang sử dụng sự phản chiếu để đến lớp bên trong, tôi đoán rằng cần phải xác định hàm tạo mặc định (cũng có thể không phải là trường hợp trong các phiên bản mới hơn), nhưng vì tôi không chắc chắn bạn có thể đúng
azerafati 4/2/2015

6
Vâng, kinh nghiệm của tôi là với Jackson 2.4.4: thực sự hàm tạo mặc định ẩn của Java là đủ. Bạn cần phải viết rõ ràng hàm tạo không có đối số chỉ khi bạn đã xác định các hàm tạo khác có tham số (nghĩa là khi Java không tạo ra hàm không có đối số cho bạn).
Jonik

2
Cũng với tôi, lớp tĩnh lưu trong ngày.
Simon

58

Tôi muốn thêm một giải pháp khác cho vấn đề này mà không yêu cầu một nhà xây dựng giả. Vì các nhà xây dựng giả là một chút lộn xộn và sau đó khó hiểu. Chúng tôi có thể cung cấp một hàm tạo an toàn và bằng cách chú thích các đối số của hàm tạo, chúng tôi cho phép jackson xác định ánh xạ giữa tham số của hàm tạo và trường.

Vì vậy, sau đây cũng sẽ làm việc. Lưu ý chuỗi bên trong chú thích phải khớp với tên trường.

import com.fasterxml.jackson.annotation.JsonProperty;
public class ApplesDO {

        private String apple;

        public String getApple() {
            return apple;
        }

        public void setApple(String apple) {
            this.apple = apple;
        }

        public ApplesDO(CustomType custom){
            //constructor Code
        }

        public ApplesDO(@JsonProperty("apple")String apple) {
        }

}

Giải pháp này đã làm được mẹo. Tôi chỉ muốn đề cập đến những điều sau đây, tôi đã có một hàm tạo, nhưng tên của các tham số khác với các tham số thể hiện, vì vậy nó không thể được ánh xạ. Thêm chú thích đã giải quyết nó, nhưng đổi tên các tham số có lẽ cũng hoạt động.
Fico

Điều gì xảy ra nếu tham số constructor không có trong phản hồi? Nó có thể được tiêm theo cách khác?
Eddie Jaoude

Dữ liệu quan trọng duy nhất đang được gửi ở đây là String apple phản hồi.
PiersyP

1
Điều này hữu ích với tôi vì tôi muốn đối tượng của mình là bất biến, vì vậy một người xây dựng giả không phải là một lựa chọn.
Jonathan Pullano

Trong trường hợp của tôi, nó cũng yêu cầu thêm @JsonCreatorvào hàm tạo với @JsonProperty.
m1ld

31

Khi tôi gặp phải vấn đề này, đó là kết quả của việc cố gắng sử dụng một lớp bên trong để phục vụ như là DO. Việc xây dựng lớp bên trong (âm thầm) yêu cầu một thể hiện của lớp kèm theo - vốn không có sẵn cho Jackson.

Trong trường hợp này, việc di chuyển lớp bên trong sang tệp .java của chính nó đã khắc phục sự cố.


6
Trong khi việc di chuyển lớp bên trong sang tệp .java của chính nó hoạt động, việc thêm công cụ sửa đổi tĩnh cũng giải quyết vấn đề như được đề cập trong câu trả lời của @ bludream.
đánh dấu

20

Nói chung, lỗi này xuất hiện do chúng tôi không tạo hàm tạo mặc định nhưng trong trường hợp của tôi Sự cố chỉ xảy ra do tôi đã tạo lớp đối tượng đã sử dụng bên trong lớp cha. Điều này đã lãng phí cả ngày của tôi.


Nó đủ để làm cho lớp lồng nhau static.
lười

13

Quy tắc ngón tay cái : Thêm một hàm tạo mặc định cho mỗi lớp bạn đã sử dụng làm lớp ánh xạ. Bạn đã bỏ lỡ điều này và vấn đề phát sinh!
Đơn giản chỉ cần thêm constructor mặc định và nó sẽ hoạt động.


câu trả lời tốt đẹp. Cảm ơn bạn. Đã lưu ngày của tôi.
Steve

9

Bạn có thể vui lòng kiểm tra cấu trúc này. Nếu tôi nhớ chính xác, bạn có thể sử dụng nó theo cách này:

{
    "applesRequest": {
        "applesDO": [
            {
                "apple": "Green Apple"
            },
            {
                "apple": "Red Apple"
            }
        ]
    }
}

Thứ hai, vui lòng thêm hàm tạo mặc định cho mỗi lớp mà nó cũng có thể giúp ích.


Không làm việc: Bắt lỗi sau: "org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Trường không được nhận & quot; applesRequest & quot; (Lớp com.smartshop.dao.AllApplesDO), không được đánh dấu là có thể bỏ"
Lucky Murari

Trước đây, nó ít nhất không thông qua lỗi đối với AllApplesDO và chỉ ném cho lớp kèm theo .. bây giờ nó ném cho chính lớp đầu tiên
Lucky Murari

Cần một constructor mặc định. Cảm ơn!
Planky

không nên chọn điều này làm câu trả lời ĐÚNG?
răng nhanh

7

Bạn phải tạo hàm tạo rỗng giả trong lớp mô hình của chúng tôi. Trong khi ánh xạ json, nó được thiết lập bằng phương thức setter.


Đây là sửa chữa.
David Kobia

Đó cũng là trường hợp của tôi. Tôi đã có nhiều hàm tạo khác nhau cho đối tượng của mình và vì vậy tôi chỉ tạo một cái rỗng khác mà dường như được sử dụng bởi jackson.
Alessandro Roaro

5

Nếu bạn bắt đầu chú thích hàm tạo, bạn phải chú thích tất cả các trường.

Lưu ý, trường Staff.name của tôi được ánh xạ tới "ANOTHER_NAME" trong chuỗi JSON.

     String jsonInString="{\"ANOTHER_NAME\":\"John\",\"age\":\"17\"}";
     ObjectMapper mapper = new ObjectMapper();
     Staff obj = mapper.readValue(jsonInString, Staff.class);
     // print to screen

     public static class Staff {
       public String name;
       public Integer age;
       public Staff() {         
       }        

       //@JsonCreator - don't need this
       public Staff(@JsonProperty("ANOTHER_NAME") String   n,@JsonProperty("age") Integer a) {
        name=n;age=a;
       }        
    }

4

Bạn phải nhận ra những lựa chọn nào Jackson có sẵn để khử lưu huỳnh. Trong Java, tên đối số phương thức không có trong mã được biên dịch. Đó là lý do tại sao Jackson thường không thể sử dụng các hàm tạo để tạo một đối tượng được xác định rõ ràng với mọi thứ đã được đặt.

Vì vậy, nếu có một hàm tạo rỗng và cũng có setters, nó sử dụng hàm tạo và setters rỗng. Nếu không có setters, một số phép thuật bóng tối (phản xạ) được sử dụng để làm điều đó.

Nếu bạn muốn sử dụng một hàm tạo với Jackson, bạn phải sử dụng các chú thích như được đề cập bởi @PiersyP trong câu trả lời của anh ấy. Bạn cũng có thể sử dụng một mô hình xây dựng. Nếu bạn gặp một số ngoại lệ, chúc may mắn. Xử lý lỗi trong Jackson hút thời gian lớn, thật khó để hiểu rằng sự vô nghĩa trong các thông báo lỗi.


Lý do bạn cần một "hàm tạo đối số mặc định" FooClass () có thể là do Spring tuân theo Đặc tả JavaBean, yêu cầu điều này hoạt động để tự động sắp xếp và không sắp xếp khi sắp xếp và giải tuần tự hóa các đối tượng.
nguyên tử88

Chà, dù sao thì việc tuần tự hóa và giải tuần tự hóa Java thành luồng nhị phân không phải là câu hỏi gì. Vì vậy, thật tốt khi Jackson cung cấp nhiều mẫu được sử dụng với quá trình khử lưu huỳnh. Tôi đặc biệt thích mẫu xây dựng vì nó cho phép đối tượng kết quả là bất biến.
Vlasec

2

Về ấn phẩm cuối cùng, tôi có cùng một vấn đề khi sử dụng Lombok 1.18. * Đã tạo ra vấn đề.

Giải pháp của tôi là thêm @NoArssConstructor (hàm tạo không có tham số), vì @Data bao gồm mặc định @RequiredArssConstructor (Trình xây dựng có tham số).

tài liệu lombok https://projectlombok.org/features/all

Điều đó sẽ giải quyết vấn đề:

package example.counter;

import javax.validation.constraints.NotNull;

import lombok.Data;

@Data
@NoArgsConstructor
public class CounterRequest {
    @NotNull
    private final Integer int1;

    @NotNull
    private final Integer int2;
}

0

Không có bộ nối tiếp / bộ giải mã jackson tùy chỉnh cũng có thể là vấn đề. Mặc dù đó không phải là trường hợp của bạn, nhưng nó đáng được đề cập.

Tôi đã đối mặt với ngoại lệ tương tự và đó là trường hợp.


0

Đối với tôi, điều này được sử dụng để làm việc, nhưng nâng cấp thư viện gây ra vấn đề này xuất hiện. Vấn đề là có một lớp học như thế này:

package example.counter;

import javax.validation.constraints.NotNull;

import lombok.Data;

@Data
public class CounterRequest {
    @NotNull
    private final Integer int1;

    @NotNull
    private final Integer int2;
}

Sử dụng lombok:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.0</version>
</dependency>

Rơi lại

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.10</version>
</dependency>

Đã khắc phục sự cố. Không chắc chắn tại sao, nhưng muốn ghi lại nó cho tương lai.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.