处理Newtonsoft.Json中的十进制值

Ric*_*per 12 javascript c# json json.net deserialization

编辑:已经差不多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
});
Run Code Online (Sandbox Code Playgroud)

它完美无瑕.

好吧,有点.

假设我有这个课程:

public class MyClass
{
    public decimal MyProp { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

如果我尝试反序列化这个json:

"{\"MyProp\": 9888.77}"
Run Code Online (Sandbox Code Playgroud)

当然它可以工作,因为它9888.77是一个Javascript浮点值.我认为.

但是我的页面中有一个屏蔽输入,使得JSON看起来像这样(对不起我的英文):

"{\"MyProp\": \"9.888,77\" }"
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

并创建了这个类:

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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但它非常难看:它只在崩溃时执行我想要的东西,并依赖于base.ReadAsDecimal()崩溃.它不可能更难看.

并不起作用:Error converting value "1.231,23" to type 'System.Nullable1[System.Decimal]'. Path 'MyProp', line X, position Y.

值本身正在转换,但也许由于某种原因,它仍然试图将字符串"1.231,23"放入小数.

那么,有没有办法正确地做到这一点?

Bri*_*ers 27

您可以使用这样的自定义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();
    }
}
Run Code Online (Sandbox Code Playgroud)

要将其插入到活页夹中,只需将转换器的实例添加到对象的Converters列表中JsonSerializerSettings:

JsonSerializerSettings settings = new JsonSerializerSettings
{
    NullValueHandling = NullValueHandling.Ignore,
    MissingMemberHandling = MissingMemberHandling.Ignore,
    Formatting = Formatting.None,
    DateFormatHandling = DateFormatHandling.IsoDateFormat,
    Converters = new List<JsonConverter> { new DecimalConverter() }
};
Run Code Online (Sandbox Code Playgroud)


小智 7

非常感谢!我一直在寻找一种解决方案,使小数始终以类似的方式进行序列化,这篇文章向我发出了正确的方向。这是我的代码:

    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);
        }
    }
Run Code Online (Sandbox Code Playgroud)