2017-05-22 2 views
1

Es gibt viele Beiträge über die Erstellung Jackson Serializer für Zahlen, Währung, etc. Für technische Anwendungen, ist es oft erforderlich, die Genauigkeit auf Zahlen basierend auf den Einheiten oder andere Kriterien.Need Jackson Serializer für Double und müssen präzisieren zur Laufzeit

Zum Beispiel können räumliche Koordinaten auf 5 oder 6 Stellen hinter dem Komma beschränkt sein, und die Temperatur kann auf 2 Stellen hinter dem Komma beschränkt sein. Standard-Serializer-Verhalten, das zu viele Ziffern oder abgeschnittene Exponentialschreibweise hat, ist nicht gut. Was ich brauche, ist etwas wie:

und noch besser in der Lage sein, die Genauigkeit zur Laufzeit anzugeben. Ich benutze auch ein MixIn. Ich könnte für jede Klasse einen Serialisierer schreiben, aber ich wollte auf bestimmte Werte festlegen.

Irgendwelche Ideen würden geschätzt.

Antwort

4

Sie können Jacksons ContextualSerializer verwenden, um die gewünschte Serialisierung zu erreichen, wie unten gezeigt.

Zunächst eine Anmerkung erstellen Präzision

@Target({ElementType.FIELD,ElementType.METHOD}) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface Precision { 
    int precision(); 
} 

Als Nächstes erstellen Sie einen Kontext Serializer für Double Art zu erfassen, die für Precision Annotation auf dem Feld sieht serialisiert werden und dann einen neuen Serializer für die angegebene Präzision erstellen.

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; 
    } 
} 

schließlich mit Anmerkungen versehen Feld benutzerdefinierte Serializer und setzen Präzision

public class Bean{ 

    @JsonSerialize(using = DoubleContextualSerializer .class) 
    @Precision(precision = 2) 
    private double doubleNumber; 

} 

hoffe, das hilft zu verwenden !!

+0

Das funktioniert fast. Übrigens haben Sie einen Tippfehler "Serilaizer". Das Problem, das ich jetzt habe, ist, dass ich ein MixIn verwende, damit ich POJOs nicht mit Jackson Annotationen verschmutze. Das MixIn sieht folgendermaßen aus: '@JsonSerialize (using = JacksonJsonDoubleSerializer.class) @FormatterPrecision (Genauigkeit = 2) abstract Double getValue();' Die '@ FormatterPrecision' ist am Ort nicht erlaubt. – smalers

+0

Dies liegt daran, dass ich die Annotation so konfiguriert hatte, dass sie nur auf Feldebene über '@Target ({ElementType.FIELD}}}} verwendet werden kann. Sie können für Methoden, Konstruktoren etc. konfigurieren. Ich habe die Antwort bearbeitet, um auch Methoden zu erlauben, indem ich 'ElementType.METHOD' hinzufüge. –

+0

Der 'ElementType.METHOD'-Vorschlag hat funktioniert. Unten ist meine Lösung, die DecimalFormat statt BigDecimal verwendet, mit hoffentlich guter Leistung. – smalers

0

verwendete ich die meisten der vorgeschlagenen Code, aber die folgende tat, die DecimalFormat verwendet die Formatierung zu tun, die den Rohtext erforderlich ausgibt:

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)); 
      } 
    } 
} 

ich MixIn verwende, so dass Klasse:

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(); 
} 

Und schließlich ermöglichen die MixIn im ObjectMapper:

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

Es funktioniert gut, um eine Genauigkeit bereitzustellen, die global auf das Datenfeld angewendet wird.

Verwandte Themen