cannot从对象值(没有委托或基于属性的创建者)中进行挑选:杰克逊为什么需要一个默认的构造函数?

问题描述 投票:0回答:1
@AllArgsConstructor @RestController @RequestMapping("/api/subject") public class SubjectController { private SubjectService subjectService; @PostMapping public void createSubject(@RequestBody SubjectCreationDTO subjectCreationDTO) { LoggingController.getLogger().info(subjectCreationDTO.getTitle()); // subjectService.createSubject(subjectCreationDTO); } }

和subjectCreationdto:
@AllArgsConstructor
@Getter
@Setter
public class SubjectCreationDTO {
    private String title;
}

因此,我在提出发布请求时会遇到此错误:

JSON分析错误:无法构建

pweb.examhelper.dto.subject.SubjectCreationDTO
(虽然至少 一个创造者存在):无法从对象值中估算(否 委托或基于财产的创建者)”

i可以通过将@noargsconstructor添加到

SubjectCreationDTO
来解决此错误,但是为什么在其他情况下,我的情况几乎完全相同。

@PostMapping public ResponseEntity<StudentDTO> createStudent(@RequestBody StudentCreationDTO studentCreationDTO) { StudentDTO savedStudent = studentService.createStudent(studentCreationDTO); return new ResponseEntity<>(savedStudent, HttpStatus.CREATED); }

这是

StudentCreationDTO
类:

@AllArgsConstructor @Getter @Setter public class StudentCreationDTO { private String username; private String firstName; private String lastName; private String email; }

我已经弄清楚了,如果有一个以上的字段,您不必指定@noargsconstructor,而杰克逊库可以从身体上解析输入json。我的问题是为什么它具有这种行为,为什么如果我没有默认构造函数,我不能解析一个字段,但是如果我有多个字段?

	

在杰克逊(Jackson)的命令中,它需要一个JSON,它需要默认的构造函数或用

@JsonCreator
java spring spring-mvc controller jackson
1个回答
4
投票
InvalidDefinitionException

。这是从对象值(没有委托或基于属性的创建者)

试图说的错误之词。
在一个默认的构造函数的情况下,杰克逊首先创建了类的默认实例,然后将每个JSON字段注入对象中。
类似,杰克逊(Jackson)首先使用
@JsonCreator用方法来实例化对象,仅用用@JsonCreator注释的方法的参数实例化对象。然后,将每个剩余字段从JSON设置为对象。注释方法可以是参数化的构造函数或静态方法。 通常,您应该只用一个

@AllArgsContructor

进行对象的序列化,但是必须有其他配置为您处理参数化的实例化。这也是来自

Baeldung

的文章,其中10.1点显示了一个典型的班级,即没有被判决,因为它既缺乏默认的构造函数或用

@JsonCreator
。 我还附加了一个示例,您可以在
核编译器
中尝试显示杰克逊在只有一个参数化的构造函数而没有默认构造函数或

@JsonCreator

方法时的行为。确切地说,该示例处理以下方案:


StudentCreationDTO1
不值得启动,因为它仅提供

@AllArgsConstructor

且不提供默认构造函数。实际上,扔了一个InvalidDefinitionException

    StudentCreationDTO2
  • 值得注意,因为它提供了一个默认的构造函数。

    StudentCreationDTO3
    值得注意,因为它提供了用
    @JsonCreator
    注释的(构造函数)方法。注释不需要包括所有类的字段。杰克逊只有几个就足以创建一个

    StudentCreationDTO3
  • 实例,然后设置其余字段。
  • public class Main {
        public static void main(String[] args) throws JsonProcessingException {
            String json = "{\n" +
                    "\t\"username\": \"johndoe\",\n" +
                    "\t\"firstName\": \"john\",\n" +
                    "\t\"lastName\": \"doe\",\n" +
                    "\t\"email\": \"[email protected]\"\n" +
                    "}";
            ObjectMapper objectMapper = new ObjectMapper();
    
            // Deserializing with no default constructor
            try {
                StudentCreationDTO1 studentCreationDTO1 = objectMapper.readValue(json, StudentCreationDTO1.class);
                System.out.println(studentCreationDTO1);
            } catch (InvalidDefinitionException e) {
                System.out.println("Throwing InvalidDefinitionException because there is no default constructor or method marked with @JsonCreator");
            }
    
            // Deserializing with default constructor
            try {
                StudentCreationDTO2 studentCreationDTO2 = objectMapper.readValue(json, StudentCreationDTO2.class);
                System.out.println("\n" + studentCreationDTO2);
            } catch (InvalidDefinitionException e) {
                System.out.println("Throwing InvalidDefinitionException because there is no default constructor or method marked with @JsonCreator");
            }
    
            // Deserializing with no default constructor but with method annotated with @JsonCreator
            try {
                StudentCreationDTO3 studentCreationDTO3 = objectMapper.readValue(json, StudentCreationDTO3.class);
                System.out.println("\n" + studentCreationDTO3);
            } catch (InvalidDefinitionException e) {
                System.out.println("Throwing InvalidDefinitionException because there is no default constructor or method marked with @JsonCreator");
            }
        }
    }
    
    @Data
    @AllArgsConstructor
    public class StudentCreationDTO1 {
        private String username;
        private String firstName;
        private String lastName;
        private String email;
    }
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class StudentCreationDTO2 {
        private String username;
        private String firstName;
        private String lastName;
        private String email;
    }
    
    @Data
    public class StudentCreationDTO3 {
        private String username;
        private String firstName;
        private String lastName;
        private String email;
    
        @JsonCreator
        public StudentCreationDTO3(@JsonProperty("username") String username, @JsonProperty("firstName") String firstName) {
            this.username = username;
            this.firstName = firstName;
        }
    }
    
    further关于杰克逊deserializtion的注释

  • 这只是一个额外的部分,它进一步发展了次要化过程的工作方式,并显示了杰克逊为何需要一台实例才能从JSON读取和设置值。我还链接了
  • Baeldung

    的一篇很棒的文章,该文章涉及以下所有序列化和挑选序列化的情况。

    如果属性有一个设置器,则通过相应的setter方法设置其值。
    
    public class MyBean {
        private String name;
    
        //... default constructor ....
    
        //... standard getName() ...
    
        // Jackson uses the corresponding setter to set the property name
        public void setName(String name) {
            this.name = name;
        }
    }
    
    
    

    如果没有提供设置器,但是类展示了用
  • @JsonSetter
和JSON属性的名称注释的方法,那么杰克逊假设注释方法是设置JSON属性的正确方法。
public class MyBean {
    private String name;

    //... default constructor ....

    //... standard getName() ...

    // Marking the method with @JsonSetter(value = "name")
    // because the JSON contains a property called 'name', 
    // but Jackson cannot find any setter in the form of setName(String)
    @JsonSetter(value = "name")
    public void setTheName(String name) {
        this.name = name;
    }
}

如果没有提供设置器或@JsonSetter方法,并且类仅显示Getter方法,那么Jackson会回到反射以设置对象的属性。

    public class MyBean { private String name; //... default constructor .... // Jackson cannot set a value with just a getter, // so it falls back on reflection to set 'name' public void getName() { return name; } }
如果类别不提供任何getter或setter,默认情况下,杰克逊仅设置公共字段或任何具有相等的可见性或以上的字段,则使用方法
ObjectMapper.setVisibility()
  • .
  • ObjectMapper mapper = new ObjectMapper(); mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY); ... public class MyBean { // Every field is set by Jackson due to Visibility.ANY public String name; proteced int id; float value; private boolean flag; //... default constructor .... //... No getters or setters .... }
	
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.