Сериализация десятичного числа для 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, который вы можете передать в конструкторе, но он работает только по типам, насколько я могу понять. JavaScriptConverter, который вы можете добавить через RegisterConverters (...), как представляется, для Dictionary.

Я хотел бы получить

{"Currency":"USD","Amount":100.31}

после сериализации. Кроме того, о переходе к двойному вопросу не может быть и речи. И мне, вероятно, нужно сделать округление (100.311 должно стать 100.31).

Кто-нибудь знает, как это сделать? Возможно ли альтернатива JavaScriptSerializer, которая позволяет более подробно управлять сериализацией?

17
05 сент. '12 в 16:53
источник поделиться
4 ответов

В первом случае 000 не наносит вреда, значение остается тем же и будет десериализовано до того же самого значения.

Во втором случае JavascriptSerializer вам не поможет. 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%.

3
05 сент. '12 в 17:36
источник

Я до сих пор не был полностью доволен всеми техническими средствами для достижения этого. 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() для примера.

12
23 сент. '14 в 18:26
источник

Я просто пережил те же проблемы, что и некоторые десятичные числа, которые были сериализованы с 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);
}
7
06 февр. '13 в 15:47
источник

Для справки в будущем это может быть достигнуто в 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, в котором Если класс должен иметь открытый конструктор без параметров. Я решил это, создав подкласс, который специфичен для формата, который я хочу.

public class SomePropertyDecimalFormatConverter : DecimalFormatJsonConverter
{
    public SomePropertyDecimalFormatConverter() : base(3)
    {
    }
}

public class Poco 
{
    [JsonConverter(typeof(SomePropertyDecimalFormatConverter))]
    public decimal SomeProperty { get;set; }
}

Пользовательский конвертер был получен из документации Json.NET.

7
12 июня '14 в 11:51
источник

Посмотрите другие вопросы по меткам или Задайте вопрос