Как правильно реализовать Spring Converter? - PullRequest
2 голосов
/ 10 марта 2020

У меня есть класс Money с фабричными методами для чисел c и строковых значений. Я хотел бы использовать его как свойство моего ввода 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
}

У меня есть конечная точка, которая ожидает a Pojo:

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

Наконец, это тело запроса:

{
   value: "100" 
}

У меня возникает следующая ошибка при попытке выполнить этот запрос:

JSON ошибка синтаксического анализа: невозможно создать экземпляр br.marcellorvalle.Money (хотя существует хотя бы один создатель): нет конструктора аргумента строки / метода фабрики для десериализации из значения строки ('100'); вложенное исключение: com.faster xml .jackson.databind.ex c .MismatchedInputException: невозможно создать экземпляр br.marcellorvalle.Money (хотя существует хотя бы один создатель): отсутствует конструктор аргумента String / метод фабрики для десериализации из значения String ('100') \ n в [Source: (PushbackInputStream); строка: 8, столбец: 19] (через цепочку ссылок: br.marcellorvalle.MoneytestPojo [\ "value \"]) ",

Если я изменю деньги и добавлю конструктор, который получает строку, запрос работает, но мне действительно нужен фабричный метод, так как я должен доставлять особые экземпляры Money в определенных c случаях (нули, нули и пустые строки).

Я что-то упустил?

Изменить: Как и просили, здесь идет класс денег:

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);
    }
//(...)
}

Ответы [ 2 ]

1 голос
/ 10 марта 2020

Аннотирование вашего фабричного метода с помощью @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, я могу предложить другой вариант - пользовательский десериализатор Джексона:

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.

0 голосов
/ 11 марта 2020

Кажется, что использование org.springframework.core.convert.converter.Converter работает только в том случае, если класс Money имеет значение «@PathVariable» в контроллере.

Наконец-то я решил ее с помощью com.faster xml .jackson.databind.util.StdConverter class:

Я создал следующие классы конвертера:

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();
        }
    }
}

Затем я аннотировал Pojo с помощью @JsonDeserialize @JsonSerialize соответственно:

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