问题描述
已经快 5 年了,我不认为这是要走的路.客户应以正确的数字格式发布数据.使用 React 或 Angular 等当前框架,或者使用适当的架构和错误处理和验证,我认为这几乎不是问题.
It's been almost 5 years and I don't think this is the way to go. The client should post the data in the correct numerical format. With current frameworks like React or Angular, or with a proper architecture and error handling & validation, i think this is almost a non-problem.
但是,如果有人想展示他们的 Json.NET 肌肉,请随时查看答案.
But if anyone wishes to flex their Json.NET muscles, feel free to check the answers.
我有一个 MVC 应用程序,我在其中处理了一些 JSON.这很简单.我的 ModelBinder 中有这段简单的代码:
I have a MVC application and I handle some JSON in it. That's simple. I have this simple piece of code in my 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:
If I try to deserialize this json:
"{"MyProp": 9888.77}"
当然可以,因为 9888.77
是 Javascript 浮点值.我想.
Of course it works, since 9888.77
is a Javascript float value. I think.
但是我的页面中有一个蒙面的钱输入,使 JSON 看起来像这样(对不起我的英语):
But I have a masked input for money in my page that makes the JSON look like this (sorry about my english):
"{"MyProp": "9.888,77" }"
AAAND,它失败了.它说它无法将字符串转换为十进制
.
AAAND, it fails. It says that it Could not convert string to decimal
.
好的,这很公平.它不是 JS 浮点数,但 Convert.ToDecimal("9.888,77")
按我想要的方式工作.
Ok, that's fair. It is not a JS float, but Convert.ToDecimal("9.888,77")
works the way I want.
我在互联网上阅读了一些关于自定义反序列化器的教程,但对我来说,为我的应用程序中的每个类定义一个自定义反序列化器是不可行的.
I've read some tutorials on the internet about custom deserializers, but its inviable for me to define a custom deserializer for every single class I have in my application.
我想要的是简单地重新定义 JSON.Net 将字符串转换为十进制属性的方式,在我想要反序列化的任何类中.我想在转换小数的过程中注入Convert.ToDecimal
函数,此时当前转换器不起作用.
What I want is to simple redefine the way JSON.Net converts a string to a decimal property, in any class i'll ever want to deserialize to. I want to inject the Convert.ToDecimal
function in the process of converting decimals, when the current converter doesn't work.
有什么办法可以做到吗?
Is there a way I could do it?
我认为有办法做到这一点,所以我稍微更改了我的代码.
I thought there was a way to do it, so I changed my code a little bit.
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<decimal>(value);
}
else { throw; }
}
else
{
throw;
}
}
}
}
但它非常丑陋:它只在崩溃时执行我想要的,并且依赖于 base.ReadAsDecimal()
崩溃.简直不能更丑了.
But it is very ugly: it executes what I want only when it crashes, and depends on base.ReadAsDecimal()
crashing. It couldn't be more ugly.
而且不起作用:将值1.231,23"转换为类型System.Nullable1[System.Decimal]"时出错.路径MyProp",X 行,Y 位置.
值本身正在被转换,但可能出于某种原因它仍然尝试将字符串1.231,23"转换为小数.
The value itself is being converted, but perhaps for some reason it still tries to put the string "1.231,23" into a decimal.
那么,有没有办法正确地做到这一点?
So, is there a way to do it properly?
推荐答案
您可以使用像这样的自定义 JsonConverter
类处理这两种格式(JSON 数字表示和屏蔽字符串格式).p>
You can handle both formats (the JSON number representation and the masked string format) using a custom JsonConverter
class like this.
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<decimal>();
}
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
列表中:
To plug this into your binder, just add an instance of the converter to the Converters
list in the JsonSerializerSettings
object:
JsonSerializerSettings settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Ignore,
Formatting = Formatting.None,
DateFormatHandling = DateFormatHandling.IsoDateFormat,
Converters = new List<JsonConverter> { new DecimalConverter() }
};
这篇关于在 Newtonsoft.Json 中处理十进制值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!