如何关闭枚举的反序列化功能

huangapple go评论77阅读模式
英文:

How to turn off deserialization for enum

问题

我想要为具体的枚举类型关闭反序列化这可能吗

练习模型类

package main.exercise;

import lombok.*;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Entity
@Builder
public class Exercise {

    @Id
    @GeneratedValue
    private int id;
    @NonNull
    private String name;
    @NonNull
    private ExerciseType exerciseType;
    private double caloriesBurned;
    private String exerciseDescription;
}

在控制器中有以下方法

@PostMapping("/addExercise")
public List<String> addExercise(@RequestBody Exercise exercise) {
    return exerciseCrudActivitiesService.addExercise(exercise);
}

该方法接受一个 Exercise 对象作为请求体如果 Exercise 类型错误我会在进行 POST http 请求时收到错误

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type 'main.exercise.ExerciseType' from String "CARdDIO": not one of the values accepted for Enum class: [CARDIO, WEIGHTLIFTING]; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type 'main.exercise.ExerciseType' from String "CARdDIO": not one of the values accepted for Enum class: [CARDIO, WEIGHTLIFTING]
 at [Source: (PushbackInputStream); line: 4, column: 25] (through reference chain: main.exercise.Exercise["exerciseType"])]

问题出在服务中的验证器它验证枚举类型是正确还是错误并将字符串返回到所有验证器的错误列表不幸的是由于错误无法达到这段代码

public List<String> addExercise(Exercise exercise) {
    ExerciseValidator validator = new ExerciseValidator();
    List<String> messages = validator.validate(exercise);
    if (messages.isEmpty()) {
        exerciseRepository.save(exercise);
    }
    return messages;
}

验证器

package main.exercise.validator.attributesvalidators;

import main.exercise.Exercise;
import main.exercise.ExerciseType;
import main.exercise.validator.ExerciseAttributesValidator;

import java.util.InputMismatchException;

public class ExerciseTypeValidator implements ExerciseAttributesValidator {

    @Override
    public String validate(Exercise exercise) {
        if (exercise.getExerciseType() == null) {
            return "You didn't put exercise type!";
        }
        try {
            ExerciseType.forName(exercise.getExerciseType().name());
        } catch (InputMismatchException e) {
            return "Wrong exercise type!";
        }

        return null;
    }
}
英文:

I would like to turn off deserialization for concrete enum. Is it possible?

Exercise model class:

package main.exercise;
import lombok.*;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Entity
@Builder
public class Exercise {
@Id
@GeneratedValue
private int id;
@NonNull
private String name;
@NonNull
private ExerciseType exerciseType;
private double caloriesBurned;
private String exerciseDescription;
}

I got method in controller:

@PostMapping(&quot;/addExercise&quot;)
public List&lt;String&gt; addExercise(@RequestBody Exercise exercise) {
return exerciseCrudActivitiesService.addExercise(exercise);
}

which takes Exercise body and if type of Exercise is wrong I got error while POST http request:

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `main.exercise.ExerciseType` from String &quot;CARdDIO&quot;: not one of the values accepted for Enum class: [CARDIO, WEIGHTLIFTING]; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `main.exercise.ExerciseType` from String &quot;CARdDIO&quot;: not one of the values accepted for Enum class: [CARDIO, WEIGHTLIFTING]
at [Source: (PushbackInputStream); line: 4, column: 25] (through reference chain: main.exercise.Exercise[&quot;exerciseType&quot;])]

The point is in service I got validator, which validates whether type of enum is right or wrong and return string to list of errors from all validators. Unfortunately this code cannot be reached because of error.

public List&lt;String&gt; addExercise(Exercise exercise) {
ExerciseValidator validator = new ExerciseValidator();
List&lt;String&gt; messages = validator.validate(exercise);
if (messages.isEmpty()) {
exerciseRepository.save(exercise);
}
return messages;
}

Validator

package main.exercise.validator.attributesvalidators;
import main.exercise.Exercise;
import main.exercise.ExerciseType;
import main.exercise.validator.ExerciseAttributesValidator;
import java.util.InputMismatchException;
public class ExerciseTypeValidator implements ExerciseAttributesValidator {
@Override
public String validate(Exercise exercise) {
if (exercise.getExerciseType() == null) {
return &quot;You didn&#39;t put exercise type!&quot;;
}
try {
ExerciseType.forName(exercise.getExerciseType().name());
} catch (InputMismatchException e) {
return &quot;Wrong exercise type!&quot;;
}
return null;
}
}

答案1

得分: 1

要关闭序列化和反序列化,您可以在exerciseType字段上添加@JsonIgnore。然而,我认为这样做并不能帮助您。

如果忽略序列化,该字段将始终为null,这不是预期的行为。

您的验证器太迟了。注意:validate方法以Exercise对象作为参数。问题出现在创建此对象的过程中。当执行到ExerciseType.forName(exercise.getExerciseType().name());这一行时,它永远不会抛出异常,因为getExerciseType()已经是有效的枚举。

除了这个自定义验证器外,您还可以利用Spring的@ControllerAdvice为该错误类型注册自己的异常处理程序。

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(InvalidFormatException.class)
    public ResponseEntity<?> badFormatException(InvalidFormatException ex, WebRequest request) {
        ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
        return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
    }
}

更多详情请参阅:https://www.springboottutorial.com/spring-boot-exception-handling-for-rest-services。

英文:

To turn off (de-)serialization, you can add the @JsonIgnore to the exerciseType field. However, I don't think this will help you anyways.

If serialization is ignored, the field would always be null which is not the intended behavior.

Your validator is too late. Note: the validate method takes an Exercise object as a parameter. The problem occurs during the creation of this object already.
When you get to the point that the line ExerciseType.forName(exercise.getExerciseType().name()); get's executed, it will NEVER throw an exception, because getExerciseType() is already a valid enum.

Instead of this custom validator, you could make use of a Spring @ControllerAdvice to register your own Exception handler for that error type.

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import com.fasterxml.jackson.databind.exc.InvalidFormatException
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(InvalidFormatException.class)
public ResponseEntity&lt;?&gt; badFormatException(InvalidFormatException ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
return new ResponseEntity&lt;&gt;(errorDetails, HttpStatus.BAD_REQUEST);
}
}

See e.g. https://www.springboottutorial.com/spring-boot-exception-handling-for-rest-services for more details.

huangapple
  • 本文由 发表于 2020年8月26日 04:51:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/63586904.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定