嵌套属性忽略 Spring Boot 转换器 [英] Spring Boot converter ignored for nested property

查看:52
本文介绍了嵌套属性忽略 Spring Boot 转换器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 TaskFilter 被用作 @RestController 中的参数:

I have a TaskFilter being used as parameter in a @RestController :

@GetMapping("/tasks")
public ResponseEntity<List<TaskDTO>> getAllTasks(Pageable pageable, TaskFilter filter)

如果我调用 GET/api/tasks?priority=low 那么它工作正常,构造函数被调用.但是,如果我使用嵌套属性 GET/api/tasks?priority.gte=low 调用它会失败.例如,如果嵌套属性是字符串,则没问题.但这里的弹簧靴似乎完全忽略了我的转换器.

If I call GET /api/tasks?priority=low then it works fine, the constructor is called. But if I call with a nested property GET /api/tasks?priority.gte=low it fails. If the nested property is a string for exemple, no problem. But here spring boots seems to completely ignore my converter.

如果映射(在另一条路由上)期望在根级别有一个 TaskPriority,则它被转换没有问题,但是当用作对象的属性时,转换不会发生,并且异常是抛出.

If the mapping (on another route) expects a TaskPriority at the root level it is converted without issue, but when used as a property of an object, the conversion does not happen and an exception is thrown.

@Getter
@Setter
@NoArgsConstructor
@ToString
public class TaskPriorityFilter extends OrderedEnumFilter<TaskPriority> {

    @JsonCreator
    public TaskPriorityFilter(String tp) {
        super(TaskPriority.forValue(tp));
    }
}

@Getter
@Setter
@NoArgsConstructor
@ToString
abstract public class OrderedEnumFilter<E extends OrderedEnum> extends AbstractComparableFilter<E> {

    public OrderedEnumFilter(E eq) {
        super(eq);
    }

    public Object getValue() {
        if (null != eq) return eq.getOrder();
        if (null != gte) return gte.getOrder();
        if (null != gt) return gt.getOrder();
        if (null != lte) return lte.getOrder();
        if (null != lt) return lt.getOrder();
        if (null != between) return between.stream().map(OrderedEnum::getOrder).collect(Collectors.toList());

        return null;
    }
}

@Getter
@Setter
@ToString
abstract public class AbstractComparableFilter<T> implements PolymorphicFilter {

    static protected AtomicInteger identifierCount = new AtomicInteger(0);

    protected T eq;
    protected T gte;
    protected T gt;
    protected T lte;
    protected T lt;
    protected List<T> between;

    protected final int id;

    public void resetIfTooHigh() {
        identifierCount.compareAndSet(Integer.MAX_VALUE - 1, 0);
    }

    public AbstractComparableFilter() {
        this.id = identifierCount.getAndIncrement();
        resetIfTooHigh();
    }

    @JsonCreator
    public AbstractComparableFilter(T eq) {
        this.eq = eq;
        this.id = identifierCount.getAndIncrement();
        resetIfTooHigh();
    }

    public String getStartKey() {
        return "betweenStart" + id;
    }

    public String getEndKey() {
        return "betweenEnd" + id;
    }

    public Object getValue() {
        if (null != eq) return eq;
        if (null != gte) return gte;
        if (null != gt) return gt;
        if (null != lte) return lte;
        if (null != lt) return lt;
        if (null != between) return between;

        return null;
    }

    public ComparableType getFilterType() {
        if (null != eq) return ComparableType.EQ;
        if (null != gte) return ComparableType.GTE;
        if (null != gt) return ComparableType.GT;
        if (null != lte) return ComparableType.LTE;
        if (null != lt) return ComparableType.LT;
        if (null != between) return ComparableType.BETWEEN;

        throw new InvalidOperationException("unknown filter subtype");
    }
}

public enum TaskPriority {
    High(75),
    Medium(50),
    Low(25)
    ;

    private final Integer order;

    TaskPriority(Integer order) {
        this.order = order;
    }

    public Integer getOrder() {
        return order;
    }

    @JsonCreator
    public static TaskPriority forValue(String value) {
        return valueOf(CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, value));
    }

    @JsonCreator
    public static TaskPriority forValue(Integer order) {
        return Stream.of(TaskPriority.values())
            .filter(c -> order.equals(c.getOrder()))
            .findFirst()
            .orElseThrow(IllegalArgumentException::new);
    }

    @JsonValue
    public String toValue() {
        return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name());
    }
}

我已声明自定义转换器,转换器已正确注册,但在转换过程中未使用:

I've declared a custom converter, the converter is correctly registered, but is not used during the conversion process :

@Configuration
public class CustomConvertersConfiguration implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new StringToTaskPriorityConverter());
    }
}

我也尝试添加 @Component 注释,但它没有改变任何东西:

I've also tried to add the @Component annotation, and it doesn't change anything :

@Component
public class StringToTaskPriorityConverter implements Converter<String, TaskPriority> {
    @Override
    public TaskPriority convert(String source) {
        return null == source ? null : TaskPriority.forValue(source);
    }
}

我之前写了一个简化版本,通过这个编辑,这里显示的代码与我的代码完全相同.

EDIT : I previously wrote a simplified version, with this edit the code presented here is exactly the same as in my code.

另外,作为一个回顾:

  • GET/api/tasks?priority=low 结果为 200
  • GET/api/tasks?priority.gte=low 结果是 400
  • GET /api/tasks?priority=low results in a 200
  • GET /api/tasks?priority.gte=low results in a 400

两者的结果都应该是 200.

推荐答案

  • 以下构造函数会导致 StringToTaskPriorityConverter 被忽略.(最初问题将 TaskPriority 作为构造函数参数,这就是我的回购行为与您的问题相反的原因)
    • Following constructor causes the StringToTaskPriorityConverter being ignored. (Initially the question had TaskPriority as constructor param, it is the reason my repo had the opposite behaviour to your question)
    •     @JsonCreator
          public TaskPriorityFilter(String tp) {
             super(TaskPriority.forValue(tp));
          }
      

      • 将构造函数替换为
      •     @JsonCreator
            public TaskPriorityFilter(TaskPriority tp) {
               super(tp);
            }
        

        • 添加了另外一个自定义转换器,如下所示.
        •    @Component
          public class StringToTaskPriorityFilterConverter implements 
                                      Converter<String, TaskPriorityFilter> {
             @Override
             public TaskPriorityFilter convert(String source) {
          
                 System.out.println("StringToTaskPriorityFilterConverter 
                                                  is called for " + source);
                 if (source == null)
                     return null;
                 try {
                     return new 
          TaskPriorityFilter(TaskPriority.forValue(Integer.parseInt(source)));
                 } catch (Exception e) {
                     return new TaskPriorityFilter(TaskPriority.forValue(source));
                 }
             }
          }
          

          • 通过上述更改,?priority=low?priority.gte=low 都有效.

            但是我不确定带有字符串参数的构造函数导致自定义转换器被忽略的原因.

            However I am not sure about the reason the Constructor with string param causes the custom converter to be ignored.

            参考

            Github 存储库,允许以下 3 个请求.https://github.com/kavi-kanap/stackoverflow-62970847

            Github repo that allows following 3 requests. https://github.com/kavi-kanap/stackoverflow-62970847

            GET tasks?priority.gte=25

            获取任务?priority=low

            这篇关于嵌套属性忽略 Spring Boot 转换器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆