javaspringjson-deserializationspring5

Parsing Object by @PostMapping, using access to methods


i tried to parse request body into Ship object

    @PostMapping("/ships")
    public Ship createShip(@RequestBody Ship ship){

        return ship;
    }

And when Ship object was deserialize, spring use only injection values to fields. But I want that spring use setters for this fields.

I tried to add annotation @JsonSetter to setters and it works well. But i think it bad way.

    @Entity
    public class Ship {

        @Id
        @GeneratedValue
        private Long id;

        private String name;
        private String planet;


        public String getName() {
            return name;
        }

        @JsonSetter
        public void setName(String name) {
            if(name == null || name == "") throw new IllegalArgumentException("Error while setting name. Can't be null and empty");
            if(name.length() > 50) throw new IllegalArgumentException("Error while setting name. Can't be mere than 50 chars");
            this.name = name;
        }

        public String getPlanet() {
            return planet;
        }

        @JsonSetter
        public void setPlanet(String planet) {
            if(planet == null || planet == "") throw new IllegalArgumentException("Error while setting planet. Can't be null and empty");
            if(planet.length() > 50) throw new IllegalArgumentException("Error while setting planet. Can't be mere than 50 chars");
            this.planet = planet;
        }

    }

Maybe exist some annotations like this:

    createShip(@RequestBody(access = METHODS) Ship ship)

or

    @Entity 
    @JsonDeserialize(access=METHODS)
    public class Ship {

Solution

  • Keep your Ship class as POJO and validate conditions in setters can be organized as spring validation feature mentioned in spring manual.

    Validating bean using spring -

    1. define custom validator
    import org.springframework.stereotype.Component;
    import org.springframework.util.StringUtils;
    import org.springframework.validation.Errors;
    import org.springframework.validation.Validator;
    
    
    @Component
    public class ShipValidator implements Validator {
    
        @Override
        public boolean supports(Class<?> clazz) {
            return Ship.class.equals(clazz);
        }
    
        @Override
        public void validate(Object obj, Errors errors) {
             Ship ship = (Ship) obj;
             String name = ship.getName();
             String planet = ship.getPlanet();
    
             if(StringUtils.isEmpty(name)) errors.rejectValue("name", "Can't be null or Empty");
             if(StringUtils.isEmpty(planet)) errors.rejectValue("planet", "Can't be null or Empty");
             if(name.length() > 50) errors.rejectValue( "name", "Can't be more than 50 chars");
             if(planet.length() > 50) errors.rejectValue("planet", "Can't be more than 50 chars");
    
        }
    
    }
    
    
    1. changes in controller to use this validator
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.validation.BindingResult;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RestController;
    
    
    @RestController
    public class ShipController {
    
        @Autowired ShipRepo shipRepo;
    
        @Autowired ShipValidator shipValidator;
    
        @PostMapping("/ships")
        public Ship saveShip(@RequestBody Ship ship, BindingResult result) {
            shipValidator.validate(ship, result);
    
            if(result.hasErrors()) {
                //TODO: add your exception handling logic and handle these errors           
                throw new IllegalArgumentException("Error in properties");
            }
    
            return shipRepo.save(ship);
        }
    }