将小数序列化为JSON,如何舍入?

我上课了

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

并希望将其序列化为JSON。 如果我使用JavaScriptSerializer我会得到

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

由于API我必须符合需要JSON数量最多两个小数位,我觉得应该可以以某种方式改变JavaScriptSerializer序列化十进制字段的方式,但我不知道如何。 您可以在构造函数中传递SimpleTypeResolver ,但只有在我能理解的情况下它才适用于类型。 你可以通过RegisterConverters(…)添加的JavaScriptConverter似乎是为Dictionary做的。

我想得到

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

在我序列化之后。 此外,改为双倍是不可能的。 我可能需要做一些舍入(100.311应该变成100.31)。

有谁知道如何做到这一点? 是否有JavaScriptSerializer的替代方案可以让您更详细地控制序列化?

在第一种情况下, 000没有损害,值仍然是相同的,并将被反序列化为完全相同的值。

在第二种情况下,JavascriptSerializer将无法帮助您。 JavacriptSerializer不应该更改数据,因为它将其序列化为众所周知的格式,它不提供成员级别的数据转换(但它提供自定义对象转换器)。 你想要的是转换+序列化,这是一个两阶段的任务。

两个建议:

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%肯定。

到目前为止,我对所有这些技术并不完全满意。 JsonConverterAttribute似乎是最有希望的,但我无法忍受硬编码参数和每个选项组合的转换器类的扩散。

所以,我提交了一个PR ,它增加了将各种参数传递给JsonConverter和JsonProperty的能力。 它已被上游接受,我预计将在下一个版本中发布(6.0.5之后的下一个版本)

然后你可以这样做:

 public class Measurements { [JsonProperty(ItemConverterType = typeof(RoundingJsonConverter))] public List Positions { get; set; } [JsonProperty(ItemConverterType = typeof(RoundingJsonConverter), ItemConverterParameters = new object[] { 0, MidpointRounding.ToEven })] public List Loads { get; set; } [JsonConverter(typeof(RoundingJsonConverter), 4)] public double Gain { get; set; } } 

有关示例,请参阅CustomDoubleRounding()测试。

为了将来参考,可以通过创建自定义JsonConverter在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修饰相关属性JsonConverterAttribute ,在这种情况下,类必须具有公共的无参数构造函数。 我通过创建一个特定于我想要的格式的子类来解决这个问题。

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

自定义转换器源自Json.NET文档 。

我只是经历了同样的麻烦,因为我有一些小数被序列化1.00和一些1.0000。 这是我的改变:

创建一个JsonTextWriter,可以将值四舍五入到4位小数。 然后,每个小数将四舍五入为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); }