处理Newtonsoft.Json中的十进制值
我有一个MVC应用程序,我在其中处理一些JSON。 这很简单。 我在ModelBinder中有这么简单的代码:
return JsonConvert.DeserializeObject(jsonString, bindingContext.ModelType, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, MissingMemberHandling = MissingMemberHandling.Ignore, Formatting = Formatting.None, DateFormatHandling = DateFormatHandling.IsoDateFormat, FloatParseHandling = FloatParseHandling.Decimal });
它完美无瑕。
好吧,有点。
假设我有这个课程:
public class MyClass { public decimal MyProp { get; set; } }
如果我尝试反序列化这个json:
"{\"MyProp\": 9888.77}"
当然它可以工作,因为9888.77
是一个Javascript浮点值。 我认为。
但是我的页面中有一个屏蔽输入,使得JSON看起来像这样(对不起我的英文):
"{\"MyProp\": \"9.888,77\" }"
AAAND,它失败了。 它说它Could not convert string to decimal
。
好的,这很公平。 它不是JS浮点数,但Convert.ToDecimal("9.888,77")
以我想要的方式工作。
我在互联网上阅读了一些关于自定义反序列化器的教程,但是我可以为我的应用程序中的每个类定义一个自定义反序列化器。
我想要的是简单地重新定义JSON.Net将字符串转换为十进制属性的方式,在任何我想要反序列化的类中。 我想在转换小数的过程中注入Convert.ToDecimal
函数,当前转换器不起作用。
有没有办法可以做到?
我认为有办法做到这一点,所以我改变了我的代码。
JsonSerializer serializer = new JsonSerializer { NullValueHandling = NullValueHandling.Ignore, MissingMemberHandling = MissingMemberHandling.Ignore, Formatting = Formatting.None, DateFormatHandling = DateFormatHandling.IsoDateFormat, FloatParseHandling = FloatParseHandling.Decimal, }; return serializer.Deserialize(new DecimalReader(jsonStr), bindingContext.ModelType);
并创建了这个类:
public class DecimalReader : JsonTextReader { public DecimalReader(string s) : base(new StringReader(s)) { } public override decimal? ReadAsDecimal() { try { return base.ReadAsDecimal(); } catch (Exception) { if (this.TokenType == JsonToken.String) { decimal value = 0; bool convertible = Decimal.TryParse(this.Value.ToString(), out value); if (convertible) { return new Nullable(value); } else { throw; } } else { throw; } } } }
但它非常难看:它只在崩溃时执行我想要的东西, 并依赖于base.ReadAsDecimal()
崩溃 。 它不可能更难看。
并且不起作用 : Error converting value "1.231,23" to type 'System.Nullable1[System.Decimal]'. Path 'MyProp', line X, position Y.
Error converting value "1.231,23" to type 'System.Nullable1[System.Decimal]'. Path 'MyProp', line X, position Y.
值本身正在转换,但也许由于某种原因,它仍然试图将字符串“1.231,23”放入小数。
那么,有没有办法正确地做到这一点?
您可以使用像这样的自定义JsonConverter
类来处理这两种格式(JSON数字表示和屏蔽字符串格式)。
class DecimalConverter : JsonConverter { public override bool CanConvert(Type objectType) { return (objectType == typeof(decimal) || objectType == typeof(decimal?)); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JToken token = JToken.Load(reader); if (token.Type == JTokenType.Float || token.Type == JTokenType.Integer) { return token.ToObject(); } if (token.Type == JTokenType.String) { // customize this to suit your needs return Decimal.Parse(token.ToString(), System.Globalization.CultureInfo.GetCultureInfo("es-ES")); } if (token.Type == JTokenType.Null && objectType == typeof(decimal?)) { return null; } throw new JsonSerializationException("Unexpected token type: " + token.Type.ToString()); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } }
要将其插入到活页夹中,只需将转换器的实例添加到JsonSerializerSettings
对象的Converters
列表中:
JsonSerializerSettings settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, MissingMemberHandling = MissingMemberHandling.Ignore, Formatting = Formatting.None, DateFormatHandling = DateFormatHandling.IsoDateFormat, Converters = new List { new DecimalConverter() } };
非常感谢! 我正在寻找一个解决方案,使得小数总是以类似的方式序列化,这篇文章发送给我正确的方向。 这是我的代码:
internal class DecimalConverter : JsonConverter { public override bool CanConvert(Type objectType) { return (objectType == typeof(decimal) || objectType == typeof(decimal?)); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { Decimal? d = default(Decimal?); if (value != null) { d = value as Decimal?; if (d.HasValue) // If value was a decimal?, then this is possible { d = new Decimal?(new Decimal(Decimal.ToDouble(d.Value))); // The ToDouble-conversion removes all unnessecary precision } } JToken.FromObject(d).WriteTo(writer); } }