【问题标题】:How to properly implement a Spring Converter?如何正确实现 Spring Converter?
【发布时间】:2020-06-22 15:54:04
【问题描述】:

我有一个 Money 类,其中包含用于数值和字符串值的工厂方法。我想将它用作我的输入 Pojos 的属性。

我为它创建了一些转换器,这是字符串之一:

@Component
public class StringMoneyConverter implements Converter<String, Money> {
    @Override
    public Money convert(String source) {
        return Money.from(source);
    }
}

我的测试 Pojo 很简单:

public class MoneyTestPojo {
   private Money value;
   //getter and setter ommited
}

我有一个需要 Pojo 的端点:

@PostMapping("/pojo")
public String savePojo(@RequestBody MoneyTestPojo pojo) {
//...
}

最后,这是请求正文:

{
   value: "100" 
}

尝试此请求时出现以下错误:

JSON 解析错误:无法构造的实例 br.marcellorvalle.Money(尽管至少有一位创作者 存在):没有要反序列化的字符串参数构造函数/工厂方法 来自字符串值('100');嵌套异常是 com.fasterxml.jackson.databind.exc.MismatchedInputException:不能 构造br.marcellorvalle.Money 的实例(虽然在 至少存在一个创建者):没有字符串参数构造函数/工厂 在 [Source: (PushbackInputStream);行:8,列:19](通过参考链: br.marcellorvalle.MoneytestPojo[\"value\"])",

如果我更改 Money 并添加一个接收 String 的构造函数,此请求有效,但我确实需要一个工厂方法,因为我必须在特定情况下(零、null 和空字符串)提供 Money 的特殊实例。

我错过了什么吗?

编辑:按照要求,这里是 Money 类:

public class Money {
    public static final Money ZERO = new Money(BigDecimal.ZERO);

    private static final int PRECISION = 2;
    private static final int EXTENDED_PRECISION = 16;
    private static final RoundingMode ROUNDING = RoundingMode.HALF_EVEN;

    private final BigDecimal amount;

    private Money(BigDecimal amount) {
        this.amount = amount;
    }

    public static Money from(float value) {
        return Money.from(BigDecimal.valueOf(value));
    }

    public static Money from(double value) {
        return Money.from(BigDecimal.valueOf(value));
    }

    public static Money from(String value) {
        if (Objects.isNull(value) || "".equals(value)) {
            return null;
        }

        return Money.from(new BigDecimal(value));
    }

    public static Money from(BigDecimal value) {
        if (Objects.requireNonNull(value).equals(BigDecimal.ZERO)) {
            return Money.ZERO;
        }

        return new Money(value);
    }
//(...)
}

【问题讨论】:

  • 你能告诉我们你的Money类吗?
  • @Joel 在最后添加了代码。
  • 谢谢。刚刚回答
  • 你解决了吗?
  • @Joel 是的...刚刚发布了一个答案。

标签: spring spring-boot jackson-databind


【解决方案1】:

似乎只有当 Money 类是控制器中的“@PathVariable”时,使用 org.springframework.core.convert.converter.Converter 才有效。

我终于用 com.fasterxml.jackson.databind.util.StdConverter 类解决了这个问题:

我创建了以下转换器类:

public class MoneyJsonConverters {
    public static class FromString extends StdConverter<String, Money> {
        @Override
        public Money convert(String value) {
            return Money.from(value);
        }
    }

    public static class ToString extends StdConverter<Money, String> {
        @Override
        public String convert(Money value) {
            return value.toString();
        }
    }
}

然后我用@JsonDeserialize @JsonSerialize 相应地注释了Pojo:

public class MoneyTestPojo {
   @JsonSerialize(converter = MoneyJsonConverters.ToString.class)
   @JsonDeserialize(converter = MoneyJsonConverters.FromString.class)
   private Money value;
   //getter and setter ommited
}

【讨论】:

    【解决方案2】:

    使用@JsonCreator(来自com.fasterxml.jackson.annotation 包)注释您的工厂方法将解决问题:

      @JsonCreator
      public static Money from(String value) {
          if (Objects.isNull(value) || "".equals(value)) {
              return null;
          }
    
          return Money.from(new BigDecimal(value));
      }
    

    我刚刚测试了它,它对我有用。除了示例请求(value 应该用引号括起来)之外,您的其余代码看起来都很好,但我想这只是一个错字。

    更新 1:

    如果您无法更改 Money 类,我可以考虑另一种选择 - 自定义 Jackson 反序列化器:

    public class MoneyDeserializer extends StdDeserializer<Money> {
    
      private static final long serialVersionUID = 0L;
    
      public MoneyDeserializer() { 
        this(null); 
      }
    
      public MoneyDeserializer(Class<?> vc) { 
        super(vc); 
      }
    
      @Override
      public Money deserialize(JsonParser jp, DeserializationContext ctxt) 
        throws IOException, JsonProcessingException {
          JsonNode node = jp.getCodec().readTree(jp);
          String value = node.textValue();
    
          return  Money.from(value);
      }
    }
    

    只需使用您的ObjectMapper 注册即可。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-01-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-27
      • 2013-07-22
      • 2021-08-17
      • 2011-07-31
      相关资源
      最近更新 更多