diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java index 68187c1302..3439372667 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java @@ -220,6 +220,13 @@ public static class Std extends FromStringDeserializer public final static int STD_INET_ADDRESS = 11; public final static int STD_INET_SOCKET_ADDRESS = 12; public final static int STD_STRING_BUILDER = 13; + /** + * If you are using java's {@linkplain Locale#toString()} serialize Locale + * (split language, country and variant with '_')
+ * You can force jackson deserialize String to Locale with java Locale style.
+ * config: {@linkplain System#setProperty(String, String) System.setProperty("jackson.std.locale.de.java", "not empty")} + */ + public final static String STD_LOCALE_DE_JAVA = "jackson.std.locale.de.java"; protected final int _kind; @@ -255,6 +262,10 @@ protected Object _deserialize(String value, DeserializationContext ctxt) throws return Pattern.compile(value); case STD_LOCALE: { + if (!System.getProperty(STD_LOCALE_DE_JAVA, "").isEmpty()) { + return deJavaLocale(value); + } + int ix = _firstHyphenOrUnderscore(value); if (ix < 0) { // single argument return new Locale(value); @@ -330,5 +341,30 @@ protected int _firstHyphenOrUnderscore(String str) } return -1; } + + /** see {@link Locale#toString()}, but ignore extends */ + private Locale deJavaLocale(String value) { + if (value.isEmpty()) { + return new Locale(""); + } + + int extStart = value.indexOf("_#"); + if (extStart != -1) value = value.substring(0, extStart); + + String language = value, country = "", variant = ""; + int pos1 = value.indexOf('_'); + if (pos1 != -1) { + language = value.substring(0, pos1++); + + int pos2 = value.indexOf('_', pos1); + if (pos2 == -1) { + country = value.substring(pos1); + } else { + country = value.substring(pos1, pos2); + variant = value.substring(pos2 + 1); + } + } + return new Locale(language, country, variant); + } } } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKStringLikeTypesTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKStringLikeTypesTest.java index 7f65dd35ef..404af9adc7 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKStringLikeTypesTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKStringLikeTypesTest.java @@ -17,6 +17,7 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.deser.std.FromStringDeserializer; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.exc.InvalidFormatException; import com.fasterxml.jackson.databind.module.SimpleModule; @@ -132,6 +133,23 @@ public void testFile() throws Exception public void testLocale() throws IOException { + // [databind #1948] + String deJavaKey = FromStringDeserializer.Std.STD_LOCALE_DE_JAVA; + String old = System.setProperty(deJavaKey, "1"); + try { + assertLocale(new Locale("")); + assertLocale(new Locale("en")); + assertLocale(new Locale("en", "US")); + assertLocale(new Locale("zh", "CN")); + assertLocale(new Locale("zh-hant", "CN")); + } finally { + if (old == null) { + System.getProperties().remove(deJavaKey); + } else { + System.setProperty(deJavaKey, old); + } + } + assertEquals(new Locale("en"), MAPPER.readValue(quote("en"), Locale.class)); assertEquals(new Locale("es", "ES"), MAPPER.readValue(quote("es_ES"), Locale.class)); assertEquals(new Locale("FI", "fi", "savo"), @@ -144,6 +162,10 @@ public void testLocale() throws IOException assertSame(Locale.ROOT, loc); } + private void assertLocale(Locale loc) throws IOException { + assertEquals(loc, MAPPER.readValue(MAPPER.writeValueAsString(loc), Locale.class)); + } + public void testCharSequence() throws IOException { CharSequence cs = MAPPER.readValue("\"abc\"", CharSequence.class);