Skip to content
Closed
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.fasterxml.jackson.databind.ser;

import java.io.IOException;
import java.math.BigDecimal;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

public class CanonicalBigDecimalSerializer extends StdSerializer<BigDecimal>
implements ValueToString<BigDecimal> {
private static final long serialVersionUID = 1L;

public static final CanonicalBigDecimalSerializer INSTANCE = new CanonicalBigDecimalSerializer();

public static final CanonicalNumberSerializerProvider PROVIDER = new CanonicalNumberSerializerProvider() {
@Override
public StdSerializer<BigDecimal> getNumberSerializer() {
return INSTANCE;
}

@Override
public ValueToString<BigDecimal> getValueToString() {
return INSTANCE;
}
};

protected CanonicalBigDecimalSerializer() {
super(BigDecimal.class);
}

@Override
public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
CanonicalNumberGenerator.verifyBigDecimalRange(value, provider);

String output = convert(value);
gen.writeNumber(output);
}

@Override
public String convert(BigDecimal value) {
// TODO Convert to exponential form if necessary
BigDecimal stripped = value.stripTrailingZeros();
int scale = stripped.scale();
String text = stripped.toPlainString();
if (scale == 0) {
return text;
}

int pos = text.indexOf('.');
int exp;
if (pos >= 0) {
exp = pos - 1;

if (exp == 0) {
return text;
}

text = text.substring(0, pos) + text.substring(pos + 1);
} else {
exp = -scale;
int end = text.length();
while (end > 0 && text.charAt(end - 1) == '0') {
end --;
}
text = text.substring(0, end);
}

if (text.length() == 1) {
return text + 'E' + exp;
}

return text.substring(0, 1) + '.' + text.substring(1) + 'E' + exp;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.fasterxml.jackson.databind.ser;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.math.BigDecimal;

import org.junit.jupiter.api.Test;

public class CanonicalBigDecimalSerializerTest {

@Test
void testCanonicalDecimalHandling_1() throws Exception {
assertSerialized("1", new BigDecimal("1"));
}

@Test
void testCanonicalDecimalHandling_1_000() throws Exception {
assertSerialized("1", new BigDecimal("1.000"));
}

@Test
void testCanonicalDecimalHandling_10_1000() throws Exception {
assertSerialized("1.01E1", new BigDecimal("10.1000"));
}

@Test
void testCanonicalDecimalHandling_1000() throws Exception {
assertSerialized("1E3", new BigDecimal("1000"));
}

@Test
void testCanonicalDecimalHandling_0_00000000010() throws Exception {
assertSerialized("0.0000000001", new BigDecimal("0.00000000010"));
}

@Test
void testCanonicalDecimalHandling_1000_00010() throws Exception {
assertSerialized("1.0000001E3", new BigDecimal("1000.00010"));
}

@Test
void testCanonicalHugeDecimalHandling() throws Exception {
BigDecimal actual = new BigDecimal("123456789123456789123456789123456789.123456789123456789123456789123456789123456789000");
assertSerialized("1.23456789123456789123456789123456789123456789123456789123456789123456789123456789E35", actual);
}

private void assertSerialized(String expected, BigDecimal actual) {
CanonicalBigDecimalSerializer serializer = new CanonicalBigDecimalSerializer();
assertEquals(expected, serializer.convert(actual));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.fasterxml.jackson.databind.ser;

import java.math.BigDecimal;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonGeneratorDecorator;

public class CanonicalJsonGeneratorDecorator implements JsonGeneratorDecorator {

private ValueToString<BigDecimal> _serializer;

public CanonicalJsonGeneratorDecorator(ValueToString<BigDecimal> serializer) {
this._serializer = serializer;
}

@Override
public JsonGenerator decorate(JsonFactory factory, JsonGenerator generator) {
return new CanonicalNumberGenerator(generator, _serializer);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.fasterxml.jackson.databind.ser;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGeneratorDecorator;
import com.fasterxml.jackson.core.PrettyPrinter;
import com.fasterxml.jackson.core.StreamWriteFeature;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.core.util.Separators;
import com.fasterxml.jackson.core.util.Separators.Spacing;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.cfg.JsonNodeFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;

public class CanonicalJsonMapper {
public static final DefaultIndenter CANONICAL_INDENTEER = new DefaultIndenter(" ", "\n");

public static final PrettyPrinter CANONICAL_PRETTY_PRINTER = new DefaultPrettyPrinter()
.withObjectIndenter(CANONICAL_INDENTEER)
.withSeparators(Separators.createDefaultInstance().withObjectFieldValueSpacing(Spacing.AFTER));

public static class Builder {
private CanonicalNumberSerializerProvider _numberSerializerProvider = CanonicalBigDecimalSerializer.PROVIDER;
private boolean _enablePrettyPrinting = false;

private Builder() {
// Don't allow to create except via builder method
}

public Builder prettyPrint() {
_enablePrettyPrinting = true;
_numberSerializerProvider = PrettyBigDecimalSerializer.PROVIDER;
return this;
}

public JsonMapper build() {
JsonGeneratorDecorator decorator = new CanonicalJsonGeneratorDecorator(_numberSerializerProvider.getValueToString());
CanonicalJsonModule module = new CanonicalJsonModule(_numberSerializerProvider.getNumberSerializer());

JsonFactory factory = JsonFactory.builder() //
.decorateWith(decorator)
.build();
JsonMapper.Builder builder = JsonMapper.builder(factory) //
.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS) //
.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) //
.enable(JsonNodeFeature.WRITE_PROPERTIES_SORTED) //
.addModule(module);

if (_enablePrettyPrinting) {
builder = builder //
.enable(SerializationFeature.INDENT_OUTPUT) //
.enable(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN) //
.defaultPrettyPrinter(CANONICAL_PRETTY_PRINTER) //
;
}

return builder.build();
}
}

public static CanonicalJsonMapper.Builder builder() {
return new Builder();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.fasterxml.jackson.databind.ser;

import java.math.BigDecimal;

import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

public class CanonicalJsonModule extends SimpleModule {
private static final long serialVersionUID = 1L;

public CanonicalJsonModule() {
this(CanonicalBigDecimalSerializer.INSTANCE);
}

public CanonicalJsonModule(StdSerializer<BigDecimal> numberSerializer) {
addSerializer(BigDecimal.class, numberSerializer);
}
}
Loading