Обработка десятичных значений в 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);
}
}