I used most of the suggested code but did the following, which uses DecimalFormat to do the formatting, which required outputting the raw text:
import java.io.IOException;
import java.text.DecimalFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
/**
* Custom serializer to serialize Double to a specified precision in output string.
* The @FormatterPrecision(precision=2) annotation needs to have been specified, for example:
* <pre>
* @JsonSerialize(using=JacksonJsonDoubleSerializer.class) @FormatterPrecision(precision=6) abstract Double getLatitude();
* </pre>
* @author sam
*
*/
public class JacksonJsonDoubleSerializer extends JsonSerializer<Double> implements ContextualSerializer {
/**
* Precision = number of digits after the decimal point to display.
* Last digit will be rounded depending on the value of the next digit.
*/
private int precision = 4;
/**
* Default constructor.
*/
public JacksonJsonDoubleSerializer ( ) {
}
/**
* Constructor.
* @param precision number of digits after the decimal to format numbers.
*/
public JacksonJsonDoubleSerializer ( int precision ) {
this.precision = precision;
}
/**
* Format to use. Create an instance so it is shared between serialize calls.
*/
private DecimalFormat format = null;
/**
*
*/
@Override
public JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property ) throws JsonMappingException {
FormatterPrecision precision = property.getAnnotation(FormatterPrecision.class);
if ( precision != null ) {
return new JacksonJsonDoubleSerializer(precision.precision());
}
return this;
}
/**
* Check that the format has been created.
*/
private DecimalFormat getFormat () {
if ( this.format == null ) {
// No format so create it
StringBuilder b = new StringBuilder("0.");
for ( int i = 0; i < this.precision; i++ ) {
b.append("0");
}
this.format = new DecimalFormat(b.toString());
}
return this.format;
}
/**
* Serialize a double
*/
@Override
public void serialize(Double value, JsonGenerator jgen, SerializerProvider provider ) throws IOException {
if ( (value == null) || value.isNaN() ) {
jgen.writeNull();
}
else {
DecimalFormat format = getFormat();
jgen.writeRawValue(format.format(value));
}
}
}
I am using a MixIn, so that class has:
public abstract class StationJacksonMixIn {
@JsonCreator
public StationJacksonMixIn () {
}
// Serializers to control formatting
@JsonSerialize(using=JacksonJsonDoubleSerializer.class)
@FormatterPrecision(precision=6) abstract Double getLatitude();
@JsonSerialize(using=JacksonJsonDoubleSerializer.class)
@FormatterPrecision(precision=6) abstract Double getLongitude();
}
And, finally, enable the MixIn in the ObjectMapper:
ObjectMapper objectMapper = new ObjectMapper().
addMixIn(Station.class,StationJacksonMixIn.class);
It works well to provide a precision where it applies globally on the data field.