Настройка MonetaryAmountFormat с использованием реализации JSR354 Moneta (JavaMoney)
Я действительно запутался в том, как настроить MonetaryAmountFormat
используя реализацию Moneta JSR-354.
Мое намерение состоит в том, чтобы иметь возможность разобрать оба 1.23
а также $3.45
как MonetaryAmount
s.
Вот мой юнит тест:
@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: для этого проанализируйте строку с номером, но без кода валюты, прочитайте и добавьте свое мнение.