web-dev-qa-db-ja.com

Double用のJacksonシリアライザーが必要で、実行時に精度を指定する必要がある

数値、通貨などのジャクソンシリアライザーの作成については多くの投稿があります。エンジニアリングアプリケーションでは、単位やその他の基準に基づいて数値の精度を設定する必要があることがよくあります。

たとえば、空間座標は小数点以下5桁または6桁に制限され、温度は小数点以下2桁に制限される場合があります。桁数が多すぎる、または指数表記が切り捨てられているデフォルトのシリアライザの動作は適切ではありません。私が必要なのは次のようなものです:

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

さらに、実行時に精度を指定できるようにすることをお勧めします。 MixInも使用しています。クラスごとにシリアライザを書くこともできましたが、特定の値を指定することを望んでいました。

任意のアイデアをいただければ幸いです。

9
smalers

以下に示すように、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;

}

お役に立てれば!!

16
Justin Jose

私は提案されたコードのほとんどを使用しましたが、フォーマットを実行するために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);

これは、データフィールドにグローバルに適用される精度を提供するために適切に機能します。

1
smalers