web-dev-qa-db-ja.com

10進数をJSONにシリアル化する方法、四捨五入する方法は?

クラスがあります

public class Money
{
    public string Currency { get; set; }
    public decimal Amount { get; set; }
}

そしてそれをJSONにシリアル化したいと思います。 JavaScriptSerializerを使用すると、

{"Currency":"USD","Amount":100.31000}

準拠しなければならないAPIのために、小数点以下最大2桁のJSON量が必要なので、JavaScriptSerializerが10進数フィールドをシリアル化する方法を何らかの方法で変更できるはずだと思いますが、その方法はわかりません。 SimpleTypeResolver がありますが、コンストラクタで渡すことができますが、私が理解できる限り、型でのみ機能します。 JavaScriptConverter は、RegisterConverters(...)で追加できますが、Dictionary用に作成されているようです。

欲しいのですが

{"Currency":"USD","Amount":100.31}

連載後。また、ダブルに変更することは問題外です。そして、おそらくいくつかの丸めを行う必要があります(100.311は100.31になるはずです)。

誰でもこれを行う方法を知っていますか?おそらく、シリアライズをより詳細に制御できるJavaScriptSerializerの代替はありますか?

24
Halvard

最初のケースでは000は害を及ぼしません。値は同じで、まったく同じ値にデシリアライズされます。

2番目のケースでは、JavascriptSerializerは役に立ちません。 JavacriptSerializerは既知の形式にデータをシリアル化するため、データを変更することは想定されていません。メンバーレベルでのデータ変換は提供されません(ただし、カスタムオブジェクトコンバーターは提供されます)。必要なのは、変換+シリアル化です。これは2段階のタスクです。

2つの提案:

1)DataContractJsonSerializerを使用:値を丸める別のプロパティを追加します。

public class Money
{
    public string Currency { get; set; }

    [IgnoreDataMember]
    public decimal Amount { get; set; }

    [DataMember(Name = "Amount")]
    public decimal RoundedAmount { get{ return Math.Round(Amount, 2); } }
}

2)値を丸めてオブジェクトのクローンを作成します。

public class Money 
{
    public string Currency { get; set; }

    public decimal Amount { get; set; }

    public Money CloneRounding() {
       var obj = (Money)this.MemberwiseClone();
       obj.Amount = Math.Round(obj.Amount, 2);
       return obj;
    }
}

var roundMoney = money.CloneRounding();

json.net でもこれは実行できないと思いますが、100%確実ではありません。

4
Marcelo De Zen

私はこれを達成するためのこれまでのすべてのテクニックに完全に満足していませんでした。 JsonConverterAttributeが最も有望であるように見えましたが、オプションのすべての組み合わせについて、ハードコードされたパラメーターとコンバータークラスの急増に耐えられませんでした。

そこで、さまざまな引数をJsonConverterとJsonPropertyに渡す機能を追加する [〜#〜] pr [〜#〜] を送信しました。それは上流で受け入れられており、次のリリース(6.0.5以降の次のリリース)に含まれる予定です。

その後、次のように実行できます。

public class Measurements
{
    [JsonProperty(ItemConverterType = typeof(RoundingJsonConverter))]
    public List<double> Positions { get; set; }

    [JsonProperty(ItemConverterType = typeof(RoundingJsonConverter), ItemConverterParameters = new object[] { 0, MidpointRounding.ToEven })]
    public List<double> Loads { get; set; }

    [JsonConverter(typeof(RoundingJsonConverter), 4)]
    public double Gain { get; set; }
}

例として CustomDoubleRounding() テストを参照してください。

18
BrandonLWhite

将来の参考のために、これはJson.netでカスタム JsonConverter を作成することでかなりエレガントに実現できます。

public class DecimalFormatJsonConverter : JsonConverter
{
    private readonly int _numberOfDecimals;

    public DecimalFormatJsonConverter(int numberOfDecimals)
    {
        _numberOfDecimals = numberOfDecimals;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var d = (decimal) value;
        var rounded = Math.Round(d, _numberOfDecimals);
        writer.WriteValue((decimal)rounded);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
        JsonSerializer serializer)
    {
        throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
    }

    public override bool CanRead
    {
        get { return false; }
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(decimal);
    }
}

コンストラクターを明示的に使用してコードでシリアライザーを作成している場合、これは正常に機能しますが、関連するプロパティを JsonConverterAttribute で装飾した方がいいと思います。この場合、クラスにはpublicが必要です、パラメータなしのコンストラクタ。これを解決するには、必要な形式に固有のサブクラスを作成します。

public class SomePropertyDecimalFormatConverter : DecimalFormatJsonConverter
{
    public SomePropertyDecimalFormatConverter() : base(3)
    {
    }
}

public class Poco 
{
    [JsonConverter(typeof(SomePropertyDecimalFormatConverter))]
    public decimal SomeProperty { get;set; }
}

カスタムコンバーターは Json.NETドキュメント から派生しています。

10
htuomola

1.00でシリアル化されている小数と1.0000でシリアル化されている小数があるため、同じ問題を経験しました。これは私の変更です:

値を小数点以下4桁に丸めることができるJsonTextWriterを作成します。その後、すべての小数は4つの小数に丸められます。1.0は1.0000になり、1.0000000も1.0000になります

private class JsonTextWriterOptimized : JsonTextWriter
{
    public JsonTextWriterOptimized(TextWriter textWriter)
        : base(textWriter)
    {
    }
    public override void WriteValue(decimal value)
    {
        // we really really really want the value to be serialized as "0.0000" not "0.00" or "0.0000"!
        value = Math.Round(value, 4);
        // divide first to force the appearance of 4 decimals
        value = Math.Round((((value+0.00001M)/10000)*10000)-0.00001M, 4); 
        base.WriteValue(value);
    }
}

標準のライターの代わりに独自のライターを使用します。

var jsonSerializer = Newtonsoft.Json.JsonSerializer.Create();
var sb = new StringBuilder(256);
var sw = new StringWriter(sb, CultureInfo.InvariantCulture);
using (var jsonWriter = new JsonTextWriterOptimized(sw))
{
    jsonWriter.Formatting = Formatting.None;
    jsonSerializer.Serialize(jsonWriter, instance);
}
8
Corneliu