Сериализация десятичного знака в 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);
}