-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
NumberFormat support #4273
base: 2.17
Are you sure you want to change the base?
NumberFormat support #4273
Conversation
src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java
Outdated
Show resolved
Hide resolved
Ah. Thank you for contributing this! One quick note: we'd need unit tests to show how this works; class Javadocs would be useful too, to indicate expectation of using format notation of |
@@ -55,6 +64,9 @@ public JsonSerializer<?> createContextual(SerializerProvider prov, | |||
if (format != null) { | |||
switch (format.getShape()) { | |||
case STRING: | |||
if (format.hasPattern()) { | |||
return new NumberSerializer(handledType(), new DecimalFormat(format.getPattern())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably needs a try-catch block for invalid format (and probably unit test to show handling), re-throw exception (using one of methods from SerializerProvider
).
src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java
Outdated
Show resolved
Hide resolved
I realized that there is a good reason why I'd like to see tests: I am pretty sure this will not work for most numeric fields :) |
|
||
public void testInvalidPattern() throws Exception { | ||
ObjectMapper mapper = newJsonMapper(); | ||
Assert.assertThrows(JsonMappingException.class, () -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you check the exception message too (so it's the expected exception)
@hurelhuyag I think we can made this work. I hope you don't mind my pushing couple of changes to streamline things. The next thing, I think, is adding bit more testing -- tests you added look good! -- for other number types; And then I think we need to resolve use of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔥
} | ||
} catch (IllegalArgumentException e) { | ||
return prov.reportBadDefinition(handledType(), | ||
String.format("Invalid `DecimalFormat`: \"%s\"", format.getPattern())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
String.format("Invalid `DecimalFormat`: \"%s\"", format.getPattern())); | |
String.format("Invalid 'DecimalFormat': \"%s\"", format.getPattern())); |
Can we use single quote instead of backtick? I looked around the codebase and in most cases (not all) single quote is used.
public void testTypeDefaults() throws Exception | ||
{ | ||
ObjectMapper mapper = newJsonMapper(); | ||
mapper.configOverride(BigDecimal.class) | ||
.setFormat(new JsonFormat.Value("00,000.00", JsonFormat.Shape.STRING, (Locale) null, (TimeZone) null, null, null)); | ||
String json = mapper.writeValueAsString(new NumberWrapper(new BigDecimal("1234"))); | ||
assertEquals(a2q("{'value':'01,234.00'}"), json); | ||
|
||
// and then read back is not supported yet. | ||
/*NumberWrapper w = mapper.readValue(a2q("{'value':'01,234.00'}"), NumberWrapper.class); | ||
assertNotNull(w); | ||
assertEquals(new BigDecimal("1234"), w.value);*/ | ||
} | ||
|
||
protected static class InvalidPatternWrapper { | ||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "#,##0.#.#") | ||
public BigDecimal value; | ||
|
||
public InvalidPatternWrapper(BigDecimal value) { | ||
this.value = value; | ||
} | ||
} | ||
|
||
public void testInvalidPattern() throws Exception { | ||
ObjectMapper mapper = newJsonMapper(); | ||
Assert.assertThrows(JsonMappingException.class, () -> { | ||
mapper.writeValueAsString(new InvalidPatternWrapper(BigDecimal.ZERO)); | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public void testTypeDefaults() throws Exception | |
{ | |
ObjectMapper mapper = newJsonMapper(); | |
mapper.configOverride(BigDecimal.class) | |
.setFormat(new JsonFormat.Value("00,000.00", JsonFormat.Shape.STRING, (Locale) null, (TimeZone) null, null, null)); | |
String json = mapper.writeValueAsString(new NumberWrapper(new BigDecimal("1234"))); | |
assertEquals(a2q("{'value':'01,234.00'}"), json); | |
// and then read back is not supported yet. | |
/*NumberWrapper w = mapper.readValue(a2q("{'value':'01,234.00'}"), NumberWrapper.class); | |
assertNotNull(w); | |
assertEquals(new BigDecimal("1234"), w.value);*/ | |
} | |
protected static class InvalidPatternWrapper { | |
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "#,##0.#.#") | |
public BigDecimal value; | |
public InvalidPatternWrapper(BigDecimal value) { | |
this.value = value; | |
} | |
} | |
public void testInvalidPattern() throws Exception { | |
ObjectMapper mapper = newJsonMapper(); | |
Assert.assertThrows(JsonMappingException.class, () -> { | |
mapper.writeValueAsString(new InvalidPatternWrapper(BigDecimal.ZERO)); | |
}); | |
} | |
protected static class InvalidPatternWrapper { | |
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "#,##0.#.#") | |
public BigDecimal value; | |
public InvalidPatternWrapper(BigDecimal value) { | |
this.value = value; | |
} | |
} | |
@Test | |
public void testTypeDefaults() throws Exception | |
{ | |
ObjectMapper mapper = newJsonMapper(); | |
mapper.configOverride(BigDecimal.class) | |
.setFormat(new JsonFormat.Value("00,000.00", JsonFormat.Shape.STRING, (Locale) null, (TimeZone) null, null, null)); | |
String json = mapper.writeValueAsString(new NumberWrapper(new BigDecimal("1234"))); | |
assertEquals(a2q("{'value':'01,234.00'}"), json); | |
// and then read back is not supported yet. | |
/*NumberWrapper w = mapper.readValue(a2q("{'value':'01,234.00'}"), NumberWrapper.class); | |
assertNotNull(w); | |
assertEquals(new BigDecimal("1234"), w.value);*/ | |
} | |
@Test | |
public void testInvalidPattern() throws Exception { | |
ObjectMapper mapper = newJsonMapper(); | |
try { | |
mapper.writeValueAsString(new InvalidPatternWrapper(BigDecimal.ZERO)); | |
fail(); | |
} catch (JsonMappingException e) { | |
verifyException(e, "expected message part 1/2"); // | |
verifyException(e, "expected message part 2/2"); | |
} | |
} |
Couple of suggestion on test if that's okay with you 🙂
- Let's use JUnit 5, (meaning remove
extends BaseMapTest
- Class declaration comes first then test methods (Check suggestion)
- As suggested above, check message by using
try-catch
andverifyException
(fromDatabindTestUtil
) combo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Above comment seems like Jackson Coding Style convention idea 🤔... WDYT, @cowtowncoder ?
@@ -55,6 +73,21 @@ public JsonSerializer<?> createContextual(SerializerProvider prov, | |||
if (format != null) { | |||
switch (format.getShape()) { | |||
case STRING: | |||
if (format.hasPattern()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (format.hasPattern()) { | |
// [databind#1114] since 2.17 : Support @JsonFormat for String, numbers, using String.format() | |
if (format.hasPattern()) { |
@@ -69,6 +102,10 @@ public JsonSerializer<?> createContextual(SerializerProvider prov, | |||
@Override | |||
public void serialize(Number value, JsonGenerator g, SerializerProvider provider) throws IOException | |||
{ | |||
if (_format != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (_format != null) { | |
// [databind#1114] since 2.17 : Support @JsonFormat for String, numbers, using String.format() | |
if (_format != null) { |
Why would you think that? Isn't the JDK doc clear? |
I think he's hinting that |
Just want to note that current PR covers subset of #1114:
Misc things that we may/may not want to also support:
But I think it's a good start. I do think that using
I'd suggest to only deal with |
Question for core maintainers: where is the doc explaining the kind of
Is the full list kept somewhere else, or they have yet to exist? |
Such a definition does not yet exist. Part of this is the practical challenge of location. For example, while it would be sort of natural from User POV to find the definition in But at the same time, finding definitions from -- say -- individual deserializers' Javadocs would not be useful at all. So I think this should probably be a combination of:
So the first thing would be to decide on (1) I think, and create a skeletal page with some of known cases, general idea behind annotations. |
No, this has nothing to do with Formatter being used, but rather that If you test, for example
you will notice that nothing added via |
Fixes #1114.