diff options
-rw-r--r-- | release-notes/CREDITS-2.x | 4 | ||||
-rw-r--r-- | release-notes/VERSION-2.x | 2 | ||||
-rw-r--r-- | src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java | 73 | ||||
-rw-r--r-- | src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java | 4 | ||||
-rw-r--r-- | src/test/java/com/fasterxml/jackson/databind/ser/jdk/BigDecimalPlain2230Test.java (renamed from src/test/java/com/fasterxml/jackson/failing/BigDecimalPlain2230Test.java) | 6 |
5 files changed, 83 insertions, 6 deletions
diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index 9bb079d17..b3b0cefe4 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -849,3 +849,7 @@ Christoph Breitkopf (bokesan@github) Alexander Saites (saites@github) * Reported #2189: `TreeTraversingParser` does not check int bounds (2.10.0) + +Pavel Chervakov (pacher@github) + * Reported #2230: `WRITE_BIGDECIMAL_AS_PLAIN` is ignored if `@JsonFormat` is used + (2.10.0) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 8ebbeb885..cb87de168 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -31,6 +31,8 @@ Project: jackson-databind #2223: Add `missingNode()` method in `JsonNodeFactory` #2227: Minor cleanup of exception message for `Enum` binding failure (reported by RightHandedMonkey@github) +#2230: `WRITE_BIGDECIMAL_AS_PLAIN` is ignored if `@JsonFormat` is used + (reported by Pavel C) #2241: Add `JsonPropertyNamingStrategy.LOWER_DOT_CASE` for dot-delimited names (contributed by zenglian@github.com) diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java index 118079836..56b30737f 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java @@ -6,8 +6,10 @@ import java.math.BigDecimal; import java.math.BigInteger; import com.fasterxml.jackson.annotation.JsonFormat; + import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; + import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; @@ -29,6 +31,11 @@ public class NumberSerializer */ public final static NumberSerializer instance = new NumberSerializer(Number.class); + /** + * Copied from `jackson-core` class `GeneratorBase` + */ + protected final static int MAX_BIG_DECIMAL_SCALE = 9999; + protected final boolean _isInt; /** @@ -48,6 +55,10 @@ public class NumberSerializer if (format != null) { switch (format.getShape()) { case STRING: + // [databind#2264]: Need special handling for `BigDecimal` + if (((Class<?>) handledType()) == BigDecimal.class) { + return bigDecimalAsStringSerializer(); + } return ToStringSerializer.instance; default: } @@ -91,8 +102,7 @@ public class NumberSerializer if (_isInt) { visitIntFormat(visitor, typeHint, JsonParser.NumberType.BIG_INTEGER); } else { - Class<?> h = handledType(); - if (h == BigDecimal.class) { + if (((Class<?>) handledType()) == BigDecimal.class) { visitFloatFormat(visitor, typeHint, JsonParser.NumberType.BIG_DECIMAL); } else { // otherwise bit unclear what to call... but let's try: @@ -100,4 +110,61 @@ public class NumberSerializer } } } -}
\ No newline at end of file + + /** + * @since 2.10 + */ + public static JsonSerializer<?> bigDecimalAsStringSerializer() { + return BigDecimalAsStringSerializer.BD_INSTANCE; + } + + final static class BigDecimalAsStringSerializer + extends ToStringSerializerBase + { + final static BigDecimalAsStringSerializer BD_INSTANCE = new BigDecimalAsStringSerializer(); + + public BigDecimalAsStringSerializer() { + super(BigDecimal.class); + } + + @Override + public boolean isEmpty(SerializerProvider prov, Object value) { + return valueToString(value).isEmpty(); + } + + @Override + public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) + throws IOException + { + final String text; + if (gen.isEnabled(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN)) { + final BigDecimal bd = (BigDecimal) value; + // 24-Aug-2016, tatu: [core#315] prevent possible DoS vector, so we need this + if (!_verifyBigDecimalRange(gen, bd)) { + // ... but wouldn't it be nice to trigger error via generator? Alas, + // no method to do that. So we'll do... + final String errorMsg = String.format( + "Attempt to write plain `java.math.BigDecimal` (see JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN) with illegal scale (%d): needs to be between [-%d, %d]", + bd.scale(), MAX_BIG_DECIMAL_SCALE, MAX_BIG_DECIMAL_SCALE); + provider.reportMappingProblem(errorMsg); + } + text = bd.toPlainString(); + } else { + text = value.toString(); + } + gen.writeString(text); + } + + @Override + public String valueToString(Object value) { + // should never be called + throw new IllegalStateException(); + } + + // 24-Aug-2016, tatu: [core#315] prevent possible DoS vector, so we need this + protected boolean _verifyBigDecimalRange(JsonGenerator gen, BigDecimal value) throws IOException { + int scale = value.scale(); + return ((scale >= -MAX_BIG_DECIMAL_SCALE) && (scale <= MAX_BIG_DECIMAL_SCALE)); + } + } +} diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java index 8af633d5b..8fd2b875f 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java @@ -2,6 +2,7 @@ package com.fasterxml.jackson.databind.ser.std; import java.io.IOException; import java.lang.reflect.Type; +import java.math.BigDecimal; import java.util.Map; import com.fasterxml.jackson.annotation.JsonFormat; @@ -97,6 +98,9 @@ public class NumberSerializers { if (format != null) { switch (format.getShape()) { case STRING: + if (((Class<?>) handledType()) == BigDecimal.class) { + return NumberSerializer.bigDecimalAsStringSerializer(); + } return ToStringSerializer.instance; default: } diff --git a/src/test/java/com/fasterxml/jackson/failing/BigDecimalPlain2230Test.java b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/BigDecimalPlain2230Test.java index 36dd49d29..fdbe0880b 100644 --- a/src/test/java/com/fasterxml/jackson/failing/BigDecimalPlain2230Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/BigDecimalPlain2230Test.java @@ -1,4 +1,4 @@ -package com.fasterxml.jackson.failing; +package com.fasterxml.jackson.databind.ser.jdk; import java.math.BigDecimal; @@ -26,12 +26,12 @@ public class BigDecimalPlain2230Test extends BaseMapTest final BigDecimal BD_VALUE = new BigDecimal(NORM_VALUE); final BigDecimalAsString INPUT = new BigDecimalAsString(BD_VALUE); // by default, use the default `toString()` - assertEquals("{\"value\":\""+BD_VALUE.toString()+"\"", MAPPER.writeValueAsString(INPUT)); + assertEquals("{\"value\":\""+BD_VALUE.toString()+"\"}", MAPPER.writeValueAsString(INPUT)); // but can force to "plain" notation final ObjectMapper m = jsonMapperBuilder() .enable(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN) .build(); - assertEquals("{\"value\":\""+NORM_VALUE+"\"", m.writeValueAsString(INPUT)); + assertEquals("{\"value\":\""+NORM_VALUE+"\"}", m.writeValueAsString(INPUT)); } } |