C# 将小数序列化为 JSON,如何四舍五入?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12283070/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Serializing a decimal to JSON, how to round off?
提问by Halvard
I have a class
我有一堂课
public class Money
{
public string Currency { get; set; }
public decimal Amount { get; set; }
}
and would like to serialize it to JSON. If I use the JavaScriptSerializerI get
并希望将其序列化为 JSON。如果我使用JavaScriptSerializer我得到
{"Currency":"USD","Amount":100.31000}
Because of the API I have to conform to needs JSON amounts with maximum two decimal places, I feel it should be possible to somehow alter the way the JavaScriptSerializerserializes a decimal field, but I can't find out how. There is the SimpleTypeResolveryou can pass in the constructor, but it only work on types as far as I can understand. The JavaScriptConverter, which you can add through RegisterConverters(...) seems to be made for Dictionary.
由于 API 我必须符合需要最多两位小数的 JSON 数量,我觉得应该可以以某种方式改变JavaScriptSerializer序列化十进制字段的方式,但我不知道如何。还有就是SimpleTypeResolver你可以在构造函数中通过,但它只能在工种,只要我能理解。您可以通过 RegisterConverters(...) 添加的JavaScriptConverter似乎是为Dictionary.
I would like to get
我想得到
{"Currency":"USD","Amount":100.31}
after I serialize. Also, changing to double is out of the question. And I probably need to do some rounding (100.311 should become 100.31).
在我序列化之后。此外,更改为 double 是不可能的。我可能需要做一些四舍五入(100.311 应该变成 100.31)。
Does anyone know how to do this? Is there perhaps an alternative to the JavaScriptSerializerthat lets you control the serializing in more detail?
有谁知道如何做到这一点?是否有替代方法JavaScriptSerializer可以让您更详细地控制序列化?
采纳答案by Marcelo De Zen
In the first case the 000does no harm, the value still is the same and will be deserialized to the exact same value.
在第一种情况下000没有坏处,该值仍然相同,并将被反序列化为完全相同的值。
In the second case the JavascriptSerializer will not help you. The JavacriptSerializeris not supposed to change the data, since it serializes it to a well-known format it does not provide data conversion at member level (but it provides custom Object converters). What you want is a conversion + serialization, this is a two-phases task.
在第二种情况下 JavascriptSerializer 不会帮助你。的JavacriptSerializer是不应该改变的数据,因为它是序列化到一个公知的格式它不提供在部件级别的数据转换(但它提供了自定义对象转换器)。你想要的是转换+序列化,这是一个两阶段的任务。
Two suggestions:
两个建议:
1) Use DataContractJsonSerializer: add another property that rounds the value:
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) Clone the object rounding the values:
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();
I guess json.netcannot do this either, but I'm not 100% sure.
我猜json.net也不能这样做,但我不是 100% 确定。
回答by Corneliu
I just went through the same trouble as I had some decimals being serialized with 1.00 and some with 1.0000. This is my change:
我刚刚遇到了同样的麻烦,因为我有一些小数被序列化为 1.00,一些小数被序列化为 1.0000。这是我的改变:
Create a JsonTextWriter that can round the value to 4 decimals. Every decimal will then be rounded to 4 decimals: 1.0 becomes 1.0000 and 1.0000000 becomes also 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);
}
}
Use your own writer instead of the standard one:
使用您自己的作家而不是标准作家:
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);
}
回答by htuomola
For future reference, this can be achieved in Json.net pretty elegantly by creating a custom 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);
}
}
If you're creating serializers in code using constructor explicitly, this will work fine but I think it's nicer to decorate the relevant properties with JsonConverterAttribute, in which case the class must have a public, parameterless constructor. I solved this by creating a subclass which is specific to the format I want.
如果您在代码中显式使用构造函数创建序列化程序,这将工作正常,但我认为用 装饰相关属性更好JsonConverterAttribute,在这种情况下,类必须具有公共的无参数构造函数。我通过创建一个特定于我想要的格式的子类来解决这个问题。
public class SomePropertyDecimalFormatConverter : DecimalFormatJsonConverter
{
public SomePropertyDecimalFormatConverter() : base(3)
{
}
}
public class Poco
{
[JsonConverter(typeof(SomePropertyDecimalFormatConverter))]
public decimal SomeProperty { get;set; }
}
The custom converter has been derived from Json.NET documentation.
自定义转换器源自Json.NET 文档。
回答by BrandonLWhite
I wasn't completely satisfied with all of the techniques thus far to achieve this. JsonConverterAttribute seemed the most promising, but I couldn't live with hard-coded parameters and proliferation of converter classes for every combination of options.
到目前为止,我对实现这一目标的所有技术并不完全满意。JsonConverterAttribute 似乎是最有前途的,但我无法忍受硬编码参数和每个选项组合的转换器类的激增。
So, I submitted a PRthat adds the ability to pass various arguments to JsonConverter and JsonProperty. It's been accepted upstream and I expect will be in the next release (whatever's next after 6.0.5)
所以,我提交了一个PR,它增加了将各种参数传递给 JsonConverter 和 JsonProperty 的能力。它已被上游接受,我希望将在下一个版本中发布(无论 6.0.5 之后是什么)
You can then do it like this:
然后你可以这样做:
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; }
}
Refer to the CustomDoubleRounding()test for an example.
有关示例,请参阅CustomDoubleRounding()测试。

