Skip to content

Commit bdf79ad

Browse files
authored
Implement #334: better Locale handling for @jsonformat (via JsonFormat.Value) (#345)
1 parent d020e4a commit bdf79ad

3 files changed

Lines changed: 60 additions & 2 deletions

File tree

release-notes/VERSION-2.x

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ NOTE: Jackson 3.x components rely on 2.x annotations; there are no separate
1919
#339: Add `OptBoolean` valued property "order" in `@JsonIncludeProperties`
2020
#342: Add `@JsonTypeInfo.writeTypeIdForDefaultImpl` to allow skipping
2121
writing of type id for values of default type
22+
#344: Improve `Locale` handling in `JsonFormat.Value`
2223

2324
2.21 (18-Jan-2026)
2425

src/main/java/com/fasterxml/jackson/annotation/JsonFormat.java

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,7 @@ public Value(String p, Shape sh, String localeStr, String tzStr, Features f,
591591
{
592592
this(p, sh,
593593
(localeStr == null || localeStr.length() == 0 || DEFAULT_LOCALE.equals(localeStr)) ?
594-
null : new Locale(localeStr),
594+
null : _parseLocale(localeStr),
595595
(tzStr == null || tzStr.length() == 0 || DEFAULT_TIMEZONE.equals(tzStr)) ?
596596
null : tzStr,
597597
null, f, lenient, radix);
@@ -606,7 +606,7 @@ public Value(String p, Shape sh, String localeStr, String tzStr, Features f,
606606
{
607607
this(p, sh,
608608
(localeStr == null || localeStr.length() == 0 || DEFAULT_LOCALE.equals(localeStr)) ?
609-
null : new Locale(localeStr),
609+
null : _parseLocale(localeStr),
610610
(tzStr == null || tzStr.length() == 0 || DEFAULT_TIMEZONE.equals(tzStr)) ?
611611
null : tzStr,
612612
null, f, lenient);
@@ -1047,5 +1047,33 @@ public boolean equals(Object o) {
10471047
&& Objects.equals(_locale, other._locale)
10481048
&& (_radix == other._radix);
10491049
}
1050+
1051+
/**
1052+
* Helper method for parsing locale string into {@link Locale},
1053+
* handling both underscore and hyphen as separator (so both
1054+
* "en_US" and "en-US" work), as well as optional variant
1055+
* (like "en_US_POSIX").
1056+
*
1057+
* @since 2.22
1058+
*/
1059+
private static Locale _parseLocale(String localeStr) {
1060+
final int len = localeStr.length();
1061+
for (int i = 0; i < len; ++i) {
1062+
char c = localeStr.charAt(i);
1063+
if (c == '_' || c == '-') {
1064+
String language = localeStr.substring(0, i);
1065+
String rest = localeStr.substring(i + 1);
1066+
// Look for second separator for variant
1067+
for (int j = 0, rlen = rest.length(); j < rlen; ++j) {
1068+
char c2 = rest.charAt(j);
1069+
if (c2 == '_' || c2 == '-') {
1070+
return new Locale(language, rest.substring(0, j), rest.substring(j + 1));
1071+
}
1072+
}
1073+
return new Locale(language, rest);
1074+
}
1075+
}
1076+
return new Locale(localeStr);
1077+
}
10501078
}
10511079
}

src/test/java/com/fasterxml/jackson/annotation/JsonFormatTest.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,35 @@ void testRadixInHashCode() {
311311
assertNotEquals(v1.hashCode(), v2.hashCode());
312312
}
313313

314+
// [annotations#344]: Locale parsing with language, country, variant
315+
@Test
316+
void testLocaleParsingWithCountry() {
317+
// Simple language-only
318+
JsonFormat.Value v = new JsonFormat.Value("", Shape.ANY, "en", "",
319+
JsonFormat.Features.empty(), null, DEFAULT_RADIX);
320+
assertEquals(new java.util.Locale("en"), v.getLocale());
321+
322+
// Language + country with underscore
323+
v = new JsonFormat.Value("", Shape.ANY, "en_US", "",
324+
JsonFormat.Features.empty(), null, DEFAULT_RADIX);
325+
assertEquals(new java.util.Locale("en", "US"), v.getLocale());
326+
327+
// Language + country with hyphen
328+
v = new JsonFormat.Value("", Shape.ANY, "en-US", "",
329+
JsonFormat.Features.empty(), null, DEFAULT_RADIX);
330+
assertEquals(new java.util.Locale("en", "US"), v.getLocale());
331+
332+
// Language + country + variant
333+
v = new JsonFormat.Value("", Shape.ANY, "en_US_POSIX", "",
334+
JsonFormat.Features.empty(), null, DEFAULT_RADIX);
335+
assertEquals(new java.util.Locale("en", "US", "POSIX"), v.getLocale());
336+
337+
// German locale
338+
v = new JsonFormat.Value("", Shape.ANY, "de_DE", "",
339+
JsonFormat.Features.empty(), null, DEFAULT_RADIX);
340+
assertEquals(new java.util.Locale("de", "DE"), v.getLocale());
341+
}
342+
314343
@Test
315344
void testRadix() {
316345
//Non-Default radix overrides the default

0 commit comments

Comments
 (0)