28

クラスがあります

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

そしてそれをJSONにシリアル化したいと思います。私が使用する場合私はJavaScriptSerializer得る

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

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

入手したい

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

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

誰かがこれを行う方法を知っていますか?シリアル化をより詳細に制御できる代替手段はおそらくありJavaScriptSerializerますか?

4

4 に答える 4

20

これを達成するためのこれまでのすべての手法に完全に満足したわけではありません。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()テストを参照してください。

于 2014-09-23T15:26:46.503 に答える
10

今後の参考のために、カスタムを作成することにより、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 class SomePropertyDecimalFormatConverter : DecimalFormatJsonConverter
{
    public SomePropertyDecimalFormatConverter() : base(3)
    {
    }
}

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

カスタム コンバーターは、 Json.NET のドキュメントから派生したものです。

于 2014-06-12T08:51:19.727 に答える
8

一部の小数が1.00でシリアル化され、一部が1.000でシリアル化されたのと同じ問題が発生しました。これが私の変更です:

値を小数点以下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);
}
于 2013-02-06T12:47:05.110 に答える
4

最初のケースで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% 確実ではありません。

于 2012-09-05T14:36:04.447 に答える