2017-05-22 33 views
1

有很多關於創建數字,貨幣等Jackson序列化器的帖子。對於工程應用程序,通常需要根據單位設置精度或其他標準。需要傑克遜串行器爲雙精度,並需要在運行時指定精度

例如,空間座標可能會限制在小數點後的5或6位數字,而溫度可能會限制在小數點後的2位數字。具有太多數字或截斷的指數表示法的默認序列化器行爲不好。我需要的是這樣的:

@JsonSerialize(using=MyDoubleSerializer.class, precision=6) double myValue; 

並且更好,但能夠在運行時指定精度。我也在使用MixIn。我可以爲每個類編寫一個序列化器,但希望指定具體的值。

任何想法,將不勝感激。

回答

4

您可以使用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; 

} 

希望這有助於!

+0

這幾乎可行。順便說一句,你有一個錯字「Serilaizer」。我現在面臨的問題是我正在使用MixIn,這樣我就不會用Jackson註釋污染POJO。 MixIn看起來像這樣:'@JsonSerialize(using = JacksonJsonDoubleSerializer.class)@FormatterPrecision(precision = 2)abstract Double getValue();'@ FormatterPrecision'在該位置不允許。 – smalers

+0

這是因爲我已經通過'@Target({ElementType.FIELD)})'配置了僅用於字段級別的註釋。你可以配置方法,構造函數等。我已經編輯了答案,通過添加'ElementType.METHOD'來允許方法。 –

+0

'ElementType.METHOD'建議工作。下面是我使用DecimalFormat而不是BigDecimal的解決方案,希望獲得良好的性能。 – smalers

0

我最常用的推薦代碼,但做了以下內容,它使用的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)); 
      } 
    } 
} 

我使用的是混入,使類有:

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:

ObjectMapper objectMapper = new ObjectMapper(). 
       addMixIn(Station.class,StationJacksonMixIn.class); 

它在數據字段全局應用時提供了精確性。