编辑:已经快5年了,我不认为这是一条路。客户端应该以正确的数字格式发布数据。使用当前的框架,如React或Angular,或者使用适当的架构和错误处理
但是如果有人想展示他们的Json.NET肌肉,请随意检查答案。
我有一个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,它失败了。它说它无法将字符串转换为十进制
。
好的,这很公平。它不是一个JS浮点数,但是Conver. ToDecimal("9.888,77")
按照我想要的方式工作。
我在Internet上阅读了一些关于自定义反序列化器的教程,但对我来说,为应用程序中的每个类定义自定义反序列化器是不可行的。
我想要的是简单地重新定义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<decimal>(value);
}
else { throw; }
}
else
{
throw;
}
}
}
}
但它非常丑陋:它只在崩溃时执行我想要的,并且依赖于base. ReadAsDecimal()
崩溃。它再丑不过了。
并且不起作用:将值“1.231,23”转换为类型“System. Nullable1[System.Decimal]”时出错。路径“MyProp”,第X行,位置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<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
列表中:
JsonSerializerSettings settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Ignore,
Formatting = Formatting.None,
DateFormatHandling = DateFormatHandling.IsoDateFormat,
Converters = new List<JsonConverter> { 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);
}
}
作为Kwaazaar答案的扩展,我还为转换器添加了反向方式(在他的示例中,它抛出了NotDatedException
。
namespace Something.Converter
{
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
/// <inheritdoc cref="JsonConverter"/>
/// <summary>
/// Converts an object to and from JSON.
/// </summary>
/// <seealso cref="JsonConverter"/>
public class DecimalConverter : JsonConverter
{
/// <summary>
/// Gets a new instance of the <see cref="DecimalConverter"/>.
/// </summary>
public static readonly DecimalConverter Instance = new DecimalConverter();
/// <inheritdoc cref="JsonConverter"/>
/// <summary>
/// Determines whether this instance can convert the specified object type.
/// </summary>
/// <param name="objectType">Type of the object.</param>
/// <returns>
/// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
/// </returns>
/// <seealso cref="JsonConverter"/>
public override bool CanConvert(Type objectType)
{
return objectType == typeof(decimal) || objectType == typeof(decimal?);
}
/// <inheritdoc cref="JsonConverter"/>
/// <summary>
/// Reads the JSON representation of the object.
/// </summary>
/// <param name="reader">The <see cref="JsonReader"/> to read from.</param>
/// <param name="objectType">Type of the object.</param>
/// <param name="existingValue">The existing value of object being read.</param>
/// <param name="serializer">The calling serializer.</param>
/// <returns>The object value.</returns>
/// <seealso cref="JsonConverter"/>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (!(reader.Value is string value))
{
if (objectType == typeof(decimal?))
{
return null;
}
return default(decimal);
}
// ReSharper disable once StyleCop.SA1126
if (decimal.TryParse(value, out var result))
{
// ReSharper disable once StyleCop.SA1126
return result;
}
if (objectType == typeof(decimal?))
{
return null;
}
return default(decimal);
}
/// <inheritdoc cref="JsonConverter"/>
/// <summary>
/// Writes the JSON representation of the object.
/// </summary>
/// <param name="writer">The <see cref="JsonWriter"/> to write to.</param>
/// <param name="value">The value.</param>
/// <param name="serializer">The calling serializer.</param>
/// <seealso cref="JsonConverter"/>
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var d = default(decimal?);
if (value != null)
{
d = value as decimal?;
if (d.HasValue)
{
d = new decimal(decimal.ToDouble(d.Value));
}
}
JToken.FromObject(d ?? 0).WriteTo(writer);
}
}
}
要将其插入您的绑定器,只需将转换器的实例添加到JsonSerializerSettings对象中的Converters列表中:
JsonSerializerSettings settings = new JsonSerializerSettings
{
// Some other settings.
Converters = new List<JsonConverter> { new DecimalConverter() }
};
或
JsonSerializerSettings settings = new JsonSerializerSettings
{
// Some other settings.
Converters = new List<JsonConverter> { DecimalConverter.Instance }
};