Java - How to make JSON date validation?

10,084

Solution 1

Well, you need to write a custom date serialisation/de-serialisation class and throw a custom exception if the date format you receive while intercepting the data is not what you'd expected.

This answer will point you in the right direction of how to do so.

Also you can have the validator in your code to validate the payload and leave Jackson to it's simple state.

Solution 2

@JsonFormat is used to set the output format when you're returning the statusDate as response.

It is better you create a DTO object that will accept String statusDate and then convert it to Date format in your controller.

To validate the date in String format, you can use @Pattern

public class StatusDateDto {

    @NotNull(message="Status date is a required field")    
    @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}", message="Invalid status date")
    private String statusDate;

    //Getter and setter
}

public ResponseEntity<?> postStatusDate(@Valid @RequestBody StatusDateDto dateDto, BindingResult result) {

        if (result.hasFieldErrors()) {
            String errors = result.getFieldErrors().stream()
                .map(p -> p.getDefaultMessage()).collect(Collectors.joining("\n"));   

            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errors);
        }

        // Convert the String to Date after validation

        return ResponseEntity.ok().build();
    }

Solution 3

Yes, you can. Let me show u the details.

First, pls create a base controller to filter all the requests like below:

package com.letv.address.controller;

import com.letv.address.utils.ConstantCode;
import com.letv.address.utils.ResponseWrapper;
import com.letv.xice.core.controller.GlobalController;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by shichaoyang on 2017/1/10.
 */
public class BaseController extends GlobalController {

public ResponseWrapper requestCheckAndPost(BindingResult result) {

    if (result.hasErrors()) {

        List<Object> errorList = new ArrayList<>();

        StringBuilder sBuilder = new StringBuilder();

        for (ObjectError error : result.getAllErrors()) {

            String fieldName = ((FieldError) error).getField();
            String fieldMessage = error.getDefaultMessage();

            sBuilder.append(fieldName)
                    .append(" ")
                    .append(getMessage(fieldMessage))
                    .append(";");

            errorList.add(fieldName);
        }

        return new ResponseWrapper(
                  ConstantCode.FAILING_WITH_ERROR_PARAM_CODE
                , errorList.toArray()
                , ""
                , sBuilder.toString()
                );
    }
    return null;
  }
}

From above code, the BindingResult will check the @JsonFormat or other components header, like @NotBlank, @Pattern and so on. If they hit the rule, they will be caught by the BindingResult and we can get the error. Below is the DTO object I used, just show it to u so that you can get more details:

package com.letv.address.controller.dto;

import com.letv.address.utils.ConstantCode;
import org.hibernate.validator.constraints.NotBlank;

/**
 * Created by shichaoyang on 2016/12/23.
 */
public class ChildrenAreaSelectRequest{

@NotBlank(message = ConstantCode.REQUEST_VALIDATE_NOT_EMPTY)
private String areaIds;

public String getAreaIds() {
    return areaIds;
}

public void setAreaIds(String areaIds) {
    this.areaIds = areaIds;
  }
}

Then in our business logic controller, we need to extend the base controller and write the codes like below:

package com.letv.address.controller;

import com.letv.address.controller.dto.ChildrenAreaSelectRequest;
import com.letv.address.controller.dto.ParentAreaSelectReqeust;
import com.letv.address.domain.Area;
import com.letv.address.service.ChildAreaService;
import com.letv.address.utils.ConstantCode;
import com.letv.address.utils.ResponseWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by shichaoyang on 2016/12/12.
 */
@RestController("areaController")
public class AreaController extends BaseController {

@Autowired
protected ChildAreaService childAreaService;

/**
 * get area info by parent id
 *
 * @param areaReqeust
 * @param result
 */
@ResponseBody
@RequestMapping(value = ConstantCode.CHILD_AREA_PATH, method = {RequestMethod.POST})
public ResponseWrapper childArea(@RequestBody @Valid ParentAreaSelectReqeust areaReqeust, BindingResult result) {

    ResponseWrapper validationWrapper = requestCheckAndPost(result);
    if (validationWrapper != null) {
        return validationWrapper;
    }

    List<Area> areaList = childAreaService.selectByParentId(areaReqeust.getParentId());
    if (areaList == null || areaList.size() == 0) {
        return new ResponseWrapper(ConstantCode.SUCCESS_WITH_EMPTY_DATA_CODE, new ArrayList<>());
    } else {
        return new ResponseWrapper(ConstantCode.SUCCESS_WITH_FILL_DATA_CODE, areaList);
    }
  }
}

By using above method, you can validate the field easily within the request. It's a beautiful way to achive this.

Hope that helps.

EDIT:

Replace the images with real codes so that anyone needs to test it.

Share:
10,084
Dvir
Author by

Dvir

Updated on June 18, 2022

Comments

  • Dvir
    Dvir almost 2 years

    I have this piece of code:

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
        protected Date statusDate;
    

    But for somehow it accepts date formats like -

    "statusDate": "2017-13-27"
    

    or

    "statusDate": "201823-12-12"
    

    Is it possible to validate the format within the request (not manually)?

    • Abhijeet
      Abhijeet over 6 years
      Is it possible to validate the format within the request? can you please elaborate? Do you mean validation in the client side while sending the date in payload?
    • Dvir
      Dvir over 6 years
      Good point - No. but throw an exception if there is an validation issue before getting the data,
    • Henry
      Henry over 6 years
      The latter one is a valid date (just a bit in the future)
    • Dvir
      Dvir over 6 years
      @Henry. Yeah, you right... but I'm not sure if the world will still exist after 9999...But that for a different topic...any way - is it possible to limit the year to 4 digits?
  • Ole V.V.
    Ole V.V. over 6 years
    Could you please paste your code into the answer? Some might want to copy-paste it.
  • CharlieShi
    CharlieShi over 6 years
    @OleV.V. of cource. Have updated. Pls check and verify. Just feel convenient to send me help.
  • Ole V.V.
    Ole V.V. over 6 years
    Looks great now, thanks (I have not tested, though).