有很多关于为数字、货币等创建 Jackson 序列化器的文章。对于工程应用程序,通常需要根据单位或其他标准设置数字的精度。
例如,空间坐标可能限制为小数点后 5 或 6 位,温度可能限制为小数点后 2 位。具有太多数字或截断指数表示法的默认序列化器行为不好。我需要的是这样的:
@JsonSerialize(using=MyDoubleSerializer.class, precision=6) double myValue;
更好的是能够在运行时指定精度。我也在使用 MixIn。我可以为每个类编写一个序列化器,但希望指定特定值。
任何想法将不胜感激。
您可以使用 Jackson 的
ContextualSerializer
来实现所需的序列化,如下所示。
首先,创建一个注释来捕获精度
@Target({ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Precision {
int precision();
}
接下来,为
Double
类型创建一个上下文序列化器,该序列化器在要序列化的字段上查找 Precision
注释,然后为指定的精度创建一个新的序列化器。
public class DoubleContextualSerializer extends JsonSerializer<Double> implements ContextualSerializer {
private int precision = 0;
public DoubleContextualSerializer (int precision) {
this.precision = precision;
}
public DoubleContextualSerializer () {
}
@Override
public void serialize(Double value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
if (precision == 0) {
gen.writeNumber(value.doubleValue());
} else {
BigDecimal bd = new BigDecimal(value);
bd = bd.setScale(precision, RoundingMode.HALF_UP);
gen.writeNumber(bd.doubleValue());
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
Precision precision = property.getAnnotation(Precision.class);
if (precision != null) {
return new DoubleContextualSerializer (precision.precision());
}
return this;
}
}
最后,注释您的字段以使用自定义序列化器并设置精度
public class Bean{
@JsonSerialize(using = DoubleContextualSerializer .class)
@Precision(precision = 2)
private double doubleNumber;
}
希望这有帮助!
我使用了大部分建议的代码,但执行了以下操作,它使用 DecimalFormat 进行格式化,这需要输出原始文本:
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));
}
}
}
我正在使用 MixIn,因此该类具有:
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();
}
最后,在 ObjectMapper 中启用 MixIn:
ObjectMapper objectMapper = new ObjectMapper().
addMixIn(Station.class,StationJacksonMixIn.class);
它可以很好地提供在数据字段上全局应用的精度。
在我最初的更复杂的尝试之后,我找到了一个效果很好的更简单的解决方案。这涉及仅在适合输出格式的情况下使用
BigDecimal
。例如,以下代码具有 getValueFormatted
方法,可将值格式化为正确的位数。在这种情况下,输出 JSON 使用短元素名称 v
而不是 value
,并且 @JsonProperty("v")
用于指示应使用 getValueFormatted
方法返回输出值。我没有放入 MixIn,但可以做到这一点。这会向普通对象添加一个方法,但这对于 REST 服务代码来说是可以的。
/**
* Return the data value.
* Ignored for JSON output because use getValueFormatted instead.
*/
@JsonIgnore
public Double getValue () {
return this.value;
}
/**
* Return the value formatted to appropriate number of digits.
* This is used with serialization to output.
* Use for JSON output because it is formatted to the correct number of digits.
* @return the data value formatted to the appropriate number of digits.
*/
@JsonProperty("v")
public BigDecimal getValueFormatted () {
// BigDecimal does not handle the concept of NaN and therefore can't serialize those values as Double does.
if ( (this.value == null) || this.value.isNaN() || this.value.isInfinite() ) {
return null;
}
BigDecimal bd = new BigDecimal(this.value).setScale(this.valueDigits,RoundingMode.HALF_UP);
return bd;
}
我在序列化浮点数时尝试了很多方法。但我没能成功。最后,我通过编写自定义序列化找到了解决方案。
根据此解决方案,所有 float 类型类都应使用 Jackson 进行序列化,以便十进制数(精度)为 4,如下所示。
自定义序列化器:
import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
public class CustomFloatSerializer extends JsonSerializer<Float> {
@Override
public void serialize(Float value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(String.format("%.4f", value));
}
}
Spring Boot 对象映射器配置 :
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
@Configuration
public class ObjectMapperConfiguration {
@Autowired
public void customize(ObjectMapper objectMapper) {
SimpleModule module = new SimpleModule();
module.addSerializer(Float.class, new CustomFloatSerializer());
objectMapper.registerModule(module);
}
}