Обработка десятичных значений в 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 является значением float Javascript. Я думаю.

но у меня есть вход в масках для страница, которая заставляет JSON выглядеть так (извините за мой английский):

"{"MyProp": "9.888,77" }"

AAAND, он терпит неудачу. Он говорит, что это Could not convert string to decimal.

ок, это справедливо. Это не поплавок JS, но Convert.ToDecimal("9.888,77") работает так, как я хочу.

я читал некоторые учебники в интернете о Custom десериализаторы, но нежизнеспособен для меня, чтобы определить пользовательский десериализатор для каждого класса у меня есть в моем приложении.

я хочу просто пересмотреть 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() авария. Хуже некуда.

и не работает :Error converting value "1.231,23" to type 'System.Nullable1[System.Decimal]'. Path 'MyProp', line X, position Y.

само значение преобразуется, но, возможно,по какой-то причине оно все еще пытается поместить строку "1.231, 23" в десятичное число.

Итак, есть ли способ, чтобы сделать это правильно?

2 ответов


вы можете обрабатывать оба формата (представление номера JSON и формат строки в маске), используя пользовательский JsonConverter класс такой.

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();
    }
}

чтобы подключить это к вашему связующему, просто добавьте экземпляр преобразователя в Converters список


Thanx много! Я искал решение, чтобы после запятой всегда сериализовать в подобной манере и этот пост послал меня в правильном направлении. Вот мой код:

    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);
        }
    }