Сериализация десятичного знака в JSON, как округлить?
у меня есть класс
public class Money
{
public string Currency { get; set; }
public decimal Amount { get; set; }
}
и хотел бы сериализовать его в JSON. Если я использую JavaScriptSerializer
Я
{"Currency":"USD","Amount":100.31000}
из-за API я должен соответствовать потребностям JSON с максимальными двумя десятичными знаками, я чувствую, что должно быть возможно как-то изменить способ JavaScriptSerializer
сериализует десятичное поле, но я не могу узнать, как это сделать. Есть SimpleTypeResolver вы можете передать в конструктор, но он работает только на типах, насколько я могу понимать. The JavaScriptConverter, который вы можете добавить через RegisterConverters(...) кажется, сделано для Dictionary
.
Я хотел бы получить
{"Currency":"USD","Amount":100.31}
после того, как я сериализовать. Кроме того, о переходе на double не может быть и речи. И мне, вероятно, нужно сделать некоторое округление (100.311 должен стать 100.31).
кто-нибудь знает, как это сделать? Возможно, есть альтернатива JavaScriptSerializer
что позволяет управлять сериализацией в более детали?
4 ответов
в первом случае 000
не вредит, значение по-прежнему то же самое и будет десериализовано до того же значения.
во втором случае JavascriptSerializer не поможет вам. The JavacriptSerializer
не должен изменять данные, так как он сериализует его в известный формат, он не обеспечивает преобразование данных на уровне члена (но он предоставляет пользовательские преобразователи объектов). То, что вы хотите, это преобразование + сериализация, это двухфазная задача.
два предложения:
1) Использовать DataContractJsonSerializer
добавить еще одно свойство, которое округляет значение:
public class Money
{
public string Currency { get; set; }
[IgnoreDataMember]
public decimal Amount { get; set; }
[DataMember(Name = "Amount")]
public decimal RoundedAmount { get{ return Math.Round(Amount, 2); } }
}
2) клонировать объект, округляя значения:
public class Money
{
public string Currency { get; set; }
public decimal Amount { get; set; }
public Money CloneRounding() {
var obj = (Money)this.MemberwiseClone();
obj.Amount = Math.Round(obj.Amount, 2);
return obj;
}
}
var roundMoney = money.CloneRounding();
Я думаю json.net не могу этого сделать, но я не уверен на 100%.
Я не был полностью удовлетворен всеми методами до сих пор для достижения этого. JsonConverterAttribute казался самым многообещающим, но я не мог жить с жестко закодированными параметрами и распространением классов конвертеров для каждой комбинации опций.
Итак, я представил PR это добавляет возможность передавать различные аргументы JsonConverter и JsonProperty. Он был принят вверх по течению, и я ожидаю, что будет в следующем выпуске (что бы ни было дальше 6.0.5)
вы можете сделать это так:
public class Measurements
{
[JsonProperty(ItemConverterType = typeof(RoundingJsonConverter))]
public List<double> Positions { get; set; }
[JsonProperty(ItemConverterType = typeof(RoundingJsonConverter), ItemConverterParameters = new object[] { 0, MidpointRounding.ToEven })]
public List<double> Loads { get; set; }
[JsonConverter(typeof(RoundingJsonConverter), 4)]
public double Gain { get; set; }
}
относятся к CustomDoubleRounding () для примера тест.
для будущей справки, этого можно достигнуть в Json.net довольно элегантно, создав пользовательский JsonConverter
public class DecimalFormatJsonConverter : JsonConverter
{
private readonly int _numberOfDecimals;
public DecimalFormatJsonConverter(int numberOfDecimals)
{
_numberOfDecimals = numberOfDecimals;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var d = (decimal) value;
var rounded = Math.Round(d, _numberOfDecimals);
writer.WriteValue((decimal)rounded);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
public override bool CanRead
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(decimal);
}
}
если вы создаете сериализаторы в коде, используя конструктор явно, это будет работать нормально, но я думаю, что лучше украсить соответствующие свойства JsonConverterAttribute
в этом случае класс должен иметь открытый конструктор без параметров. Я решил это, создав подкласс, который специфичен для формата I хотеть.
public class SomePropertyDecimalFormatConverter : DecimalFormatJsonConverter
{
public SomePropertyDecimalFormatConverter() : base(3)
{
}
}
public class Poco
{
[JsonConverter(typeof(SomePropertyDecimalFormatConverter))]
public decimal SomeProperty { get;set; }
}
пользовательский конвертер был получен из Json.NET документация.
Я просто прошел через те же проблемы, что и у меня были некоторые десятичные дроби, сериализованные с 1.00, а некоторые с 1.0000. Это:
создайте JsonTextWriter, который может округлить значение до 4 десятичных знаков. Каждый десятичный будет округлен до 4 десятичных знаков: 1.0 становится 1.0000 и 1.0000000 становится также 1.0000
private class JsonTextWriterOptimized : JsonTextWriter
{
public JsonTextWriterOptimized(TextWriter textWriter)
: base(textWriter)
{
}
public override void WriteValue(decimal value)
{
// we really really really want the value to be serialized as "0.0000" not "0.00" or "0.0000"!
value = Math.Round(value, 4);
// divide first to force the appearance of 4 decimals
value = Math.Round((((value+0.00001M)/10000)*10000)-0.00001M, 4);
base.WriteValue(value);
}
}
используйте свой собственный писатель вместо стандартного:
var jsonSerializer = Newtonsoft.Json.JsonSerializer.Create();
var sb = new StringBuilder(256);
var sw = new StringWriter(sb, CultureInfo.InvariantCulture);
using (var jsonWriter = new JsonTextWriterOptimized(sw))
{
jsonWriter.Formatting = Formatting.None;
jsonSerializer.Serialize(jsonWriter, instance);
}