Настройка MonetaryAmountFormat с использованием реализации JSR354 Moneta (JavaMoney)

Я действительно запутался в том, как настроить MonetaryAmountFormat используя реализацию Moneta JSR-354.

Мое намерение состоит в том, чтобы иметь возможность разобрать оба 1.23 а также $3.45 как MonetaryAmounts.

Вот мой юнит тест:

@Test
public void testString() {
    Bid bid = new Bid("1.23");
    assertEquals(1.23, bid.getValue(), 0.0);
    System.out.println(bid);

    bid = new Bid("$3.45");
    assertEquals(3.45, bid.getValue(), 0.0);
    System.out.println(bid);
}

Вот мой класс:

public final class Bid {

    private static final CurrencyUnit USD = Monetary.getCurrency("USD");
    private MonetaryAmount bid;

    /**
     * Constructor.
     *
     * @param bidStr the bid
     */
    public Bid(String bidStr) {
        MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(
                AmountFormatQueryBuilder.of(Locale.US)
                .set("pattern", "###.##")
                .build());
        if (StringUtils.isNotBlank(bidStr)) {
            bidStr = bidStr.trim();
            bidStr = bidStr.startsWith("$") ? bidStr.substring(1) : bidStr;
            try {
                bid = FastMoney.parse(bidStr, format);
            } catch (NumberFormatException e) {
                bid = FastMoney.of(0, USD);
            }
        }
    }

    /**
     * Constructor.
     *
     * @param bidDouble the bid
     */
    public Bid(double bidDouble) {
        bid = FastMoney.of(bidDouble, USD);
    }

    public double getValue() {
        return bid.getNumber().doubleValue();
    }
}

Мне бы очень хотелось иметь возможность разобрать bidStr с или без $ используя сингл MonetaryAmountFormat, но потратив много времени, пытаясь выяснить, как сделать $ по желанию я сдался. К сожалению, я даже не могу понять, как сделать 1.23 разбираться как USD. Монета кидает NullPointerException, Я должен установить валюту, используя AmountFormatQueryBuilder? Каковы все ключи, которые могут быть установлены с помощью AmountFormatQueryBuilder? Я искал документацию, но нигде не мог найти ничего, кроме пары общих ключей, таких как pattern,

1 ответ

Решение

JavaMoney не очень хорошо работает при попытке разобрать неквалифицированные числа (например, 1.23).

По умолчанию MonetaryAmountFormat хочет, чтобы вы указали название валюты (например, USD 1.23):

MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.US);
MonetaryAmount amount = format.parse("USD 1.23");

Если вы установите CurrencyStyle.SYMBOL, то вы можете разобрать по названию валюты или символу (так, либо USD 1.23 или же $3.45):

AmountFormatQuery formatQuery = AmountFormatQueryBuilder.of(Locale.US)
    .set(CurrencyStyle.SYMBOL)
    .build();
MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(formatQuery);
MonetaryAmount amount1 = format.parse("USD 1.23");
MonetaryAmount amount2 = format.parse("$3.45");

Это, вероятно, самое близкое, что вы собираетесь получить с JavaMoney.

Если вы знаете, что получаете только числа из одной и той же валюты, возможно, вам лучше было бы проанализировать строку в другом месте (с помощью регулярных выражений или чего-то подобного) и преобразовать в согласованный формат ввода, потому что (как вы обнаружили) вы легко получаете NullPointerExceptionбез объяснения причин, когда JavaMoney несчастлив.

Что касается доступных ключей, вы можете посмотреть на константы на org.javamoney.moneta.format.AmountFormatParams: pattern, groupingSizes, groupingSeparators,

При настройке формата важно отметить, что вы должны использовать общий знак валюты: ¤, Например, вы можете сделать .set("pattern", "¤#,##0.00"),

Наконец, вместо непосредственного использования FastMoney класс от Moneta RI, вы можете разобрать прямо с MonetaryAmountFormat:

// rather than using Monata directly:
MonetaryAmount amount = FastMoney.parse(bidStr, format);

// use the API:
MonetaryAmount amount = format.parse(bidStr);

Если вы используете шаблон нестандартного формата, то вы можете анализировать строки, которые содержат только число без валюты, но вы должны указать ожидаемую валюту самостоятельно. Вот пример:

@Test
public void testParse_pattern_without_currency_sign_but_with_currency_in_context() {
    CurrencyUnit usd = Monetary.getCurrency("USD");
    AmountFormatContextBuilder builder = AmountFormatContextBuilder.of(US);
    builder.set(AmountFormatParams.PATTERN, "0.00"); // the pattern doesn't contains a currency sign ¤
    builder.set(CurrencyUnit.class, usd); // set expected currency
    AmountFormatContext context = builder.build();
    DefaultMonetaryAmountFormat format = new DefaultMonetaryAmountFormat(context);
    MonetaryAmount parsedAmount = format.parse("0.01 USD");
    assertSame(parsedAmount.getCurrency(), usd);
    assertEquals(parsedAmount.getNumber().doubleValueExact(), 0.01D);
    assertEquals(parsedAmount.toString(), "USD 0.01");
}

Обратите внимание, что вам нужно создать контекст с set(CurrencyUnit.class, usd),

Но если вы попытаетесь установить шаблон со знаком валюты, например, 0.00 ¤ тогда вы получите ошибку: javax.money.format.MonetaryParseException: Error parsing CurrencyUnit: no input,

Я создал тикет Lenient Formatter: для этого проанализируйте строку с номером, но без кода валюты, прочитайте и добавьте свое мнение.

Другие вопросы по тегам