2012-09-05 111 views
17

は、私はクラス小数点以下をJSONにシリアル化する方法は?

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

を持っており、JSONにそれをシリアライズしたいと思います。私はので、私はJSONが最大2つの小数点以下で金額のニーズに合致しなければならないAPIの

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

を取得JavaScriptSerializerを使用している場合、私は、何らかの形でJavaScriptSerializerは10進数フィールドをシリアライズする方法を変更することが可能なはずであると感じしかし、私はどのように見つけることができません。 SimpleTypeResolverがコンストラクタに渡すことができますが、それは私が理解できる限り型でのみ動作します。 RegisterConverters(...)を使用して追加できるJavaScriptConverterは、Dictionaryのように見えます。

私はシリアライズ後

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

を取得したいと思います。また、ダブルに変更することは問題外です。そして、おそらく丸めをする必要があります(100.311は100.31になるはずです)。

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

+0

シリアライズ時にのみ数値を丸めたいですか?クラスに別のプロパティを追加し、Amountプロパティの代わりにそのプロパティをシリアル化し、元のAmountプロパティをシリアル化しないようにマークすることもできます。 –

+0

@ MarkSherrettaはい、私はJSONにシリアル化するときにのみ丸めたいです。それを文字列にすることなく(「金額」:「100.31」)それを行うことはできますか?シリアル化のための丸められた二重のフィールド? – Halvard

+0

私は変更を1か所だけにすることをお勧めします。私の場合は、(このオブジェクトだけでなく)直列化されているすべての小数点についてそれを行うことはOKです。 – Halvard

答えて

3

最初のケースでは、000は問題ありません。値は同じであり、まったく同じ値にデシリアライズされます。

2番目のケースでは、JavascriptSerializerは役に立ちません。 JavacriptSerializerは、メンバレベルでデータ変換を提供しない(ただし、カスタムオブジェクトコンバータを提供する)既知の形式にシリアル化するため、データを変更することは想定されていません。あなたが望むのは変換+シリアライゼーションですが、これは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%確実ではありません。

+0

私の特定のケースでは、受信部分(私が何もコントロールしていない)が2つ以上の小数点で妥当性検査エラーを投げるので、 '000'は害を及ぼします。それを除いて、私はあなたのソリューションを確かに考えます。 – Halvard

+1

あなたの答えをありがとう:)私はクローニング提案(私は 'DataContractJsonSerializer'提案のために行きました、それはうまく動作します)をチェックしていなくても修正するように設定しています。 – Halvard

7

私はちょうど私がいくつか小数点以下を1.00でシリアル化していたのと同じトラブルを経験しました。 これは私の変更です:

値を四捨五入することができるJsonTextWriterを作成します。すべての小数は、4つの小数に丸められます:1.0ではなく、標準の一つで、独自のライターを使用し

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

も1.0000となり1.0000と1.0000000次のようになります。

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

私はこのソリューションをテストしましたが、それも機能します。 'var jsonSerializer = Newtonsoft.Json.JsonSerializer.Create();'を 'var jsonSerializer = Newtonsoft.Json.JsonSerializer.Create(new JsonSerializerSettings());'に変更してコンパイルする必要がありました。おそらくそれは私が.NETフレームワーク4.5上にいるためですか? – Halvard

8

今後の参考のために、これを達成することができますJsonであなたが明示的にコンストラクタを使用してコード内のシリアライザを作成している場合は、ネットはかなりエレガントなカスタムを作成することによって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 documentationから派生しました。

+0

これを有効にするために他に何か必要なことはありますか?クラスと属性を追加しましたが、WriteJson関数には絶対に入りません。 – NickG

+0

@NickGそうすべきではありません。私はちょうど新しいコンソールアプリと最新のJson.NETで確認しました。私は2つのコンバータクラスとここで定義したPocoと 'JsonConvert.SerializeObject(新しいPoco {SomeProperty = 1.23456789m});' {"SomeProperty":1.235}のシリアル化された値を返しました。 – htuomola

+0

私は、サプライヤのJSONの奇妙な値をより賢明な値に変換することを望んでいました。これは、JSONを作成するときにのみ機能します。例えば57.400000000000000001など...しかし、私は今これをbizロジックコードで行ってきました。 – NickG

12

これを達成するためのこれまでのすべての技術には完全に満足していませんでした。 JsonConverterAttributeが最も有望なように見えましたが、ハードコーディングされたパラメータや、オプションの組み合わせごとにコンバータークラスが増えてしまっていました。

私はPRを提出し、JsonConverterとJsonPropertyにさまざまな引数を渡す機能を追加しました。また、上流受け入れ、私は次のリリース(どんなの次の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()テストを参照してください。

+0

私はバージョン8でこの仕事をすることはできません。この仕事をするために必要なものの完全な例はありますか?コードは正常に実行されますが、何も丸めません。 – NickG

+0

ああ、デシリアライズ時にはうまくいかないことに気づいた。新しいJSONにシリアライズを作成するときのみ:( – NickG

+0

CanReadをtrueに戻してから、正しいReadJsonを実装してみてください。双方向。 – BrandonLWhite

関連する問題