Я тестировал Json. NET сериализацию корзины покупок, над которой я работаю, и заметил, что когда я сериализирую -> десериализовать -> снова сериализую, я получаю разницу в форматировании конечного нуля некоторых из полей decimal
. Вот код сериализации:
private static void TestRoundTripCartSerialization(Cart cart)
{
string cartJson = JsonConvert.SerializeObject(cart, Formatting.Indented);
Console.WriteLine(cartJson);
Cart cartClone = JsonConvert.DeserializeObject<Cart>(cartJson);
string cloneJson = JsonConvert.SerializeObject(cartClone, Formatting.Indented);
Console.WriteLine(cloneJson);
Console.WriteLine("\r\n Serialized carts are " + (cartJson == cloneJson ? "" : "not") + " identical");
}
Cart
реализует IEnumerable<T>
и имеет JsonObjectAttribute
, чтобы позволить ему сериализоваться как объект, включая его свойства, а также его внутренний список. Свойства decimal
Cart
не изменяются, но некоторые свойства decimal
объектов и их внутренних объектов во внутреннем списке / массиве действуют так же, как в этом фрагменте кода, приведенного выше:
Первый раз сериализации:
...
"Total": 27.0000,
"PaymentPlan": {
"TaxRate": 8.00000,
"ManualDiscountApplied": 0.0,
"AdditionalCashDiscountApplied": 0.0,
"PreTaxDeposit": 25.0000,
"PreTaxBalance": 0.0,
"DepositTax": 2.00,
"BalanceTax": 0.0,
"SNPFee": 25.0000,
"cartItemPaymentPlanTypeID": "SNP",
"unitPreTaxTotal": 25.0000,
"unitTax": 2.00
}
}
],
}
Второй раз сериализации:
...
"Total": 27.0,
"PaymentPlan": {
"TaxRate": 8.0,
"ManualDiscountApplied": 0.0,
"AdditionalCashDiscountApplied": 0.0,
"PreTaxDeposit": 25.0,
"PreTaxBalance": 0.0,
"DepositTax": 2.0,
"BalanceTax": 0.0,
"SNPFee": 25.0,
"cartItemPaymentPlanTypeID": "SNP",
"unitPreTaxTotal": 25.0,
"unitTax": 2.0
}
}
],
}
Обратите внимание на Total
, TaxRate
, а некоторые другие изменили с четырех конечных нулей на одиночный конечный ноль. В какой-то момент я нашел кое-что относительно изменений в обработке конечных нулей в исходном коде, но ничего такого, что я понял достаточно хорошо, чтобы соединить это. Я не могу поделиться полной реализацией Cart здесь, но я создал ее голую модель и не смог воспроизвести результаты. Наиболее очевидные различия заключались в том, что моя голая версия потеряла некоторое дополнительное наследование / реализацию абстрактных базовых классов и интерфейсов и некоторое использование обобщенного типа c для них (где параметр типа generi c определяет тип некоторых из вложенных дочерних объектов ).
Так что я надеюсь, что без этого кто-то еще может ответить: Есть идеи, почему меняются конечные нули? После десериализации строки JSON объекты кажутся идентичными оригиналу, но я хочу убедиться, что в Json. NET нет чего-то такого, что может привести к потере точности или округлению, которое может постепенно изменить одно из следующих значений: эти десятичные дроби после многих циклических сериализаций.
Обновлено
Вот воспроизводимый пример. Я думал, что исключил JsonConverter
, но ошибся. Поскольку мой внутренний список _items
напечатан на интерфейсе, я должен сказать Json. NET, к какому конкретному типу следует вернуться обратно. Я не хотел фактических Type
имен в JSON, поэтому вместо использования TypeNameHandling.Auto
я дал элементам уникальное свойство идентификатора строки. JsonConverter
использует это, чтобы выбрать конкретный тип для создания, но я думаю, JObject
уже проанализировал мои decimal
с double
с? Возможно, я второй раз внедряю JsonConverter
, и у меня нет полного понимания того, как они работают, потому что найти документацию было сложно. Так что я могу ошибаться ReadJson
.
[JsonObject]
public class Test : IEnumerable<IItem>
{
[JsonProperty(ItemConverterType = typeof(TestItemJsonConverter))]
protected List<IItem> _items;
public Test() { }
[JsonConstructor]
public Test(IEnumerable<IItem> o)
{
_items = o == null ? new List<IItem>() : new List<IItem>(o);
}
public decimal Total { get; set; }
IEnumerator IEnumerable.GetEnumerator()
{
return _items.GetEnumerator();
}
IEnumerator<IItem> IEnumerable<IItem>.GetEnumerator()
{
return _items.GetEnumerator();
}
}
public interface IItem
{
string ItemName { get; }
}
public class Item1 : IItem
{
public Item1() { }
public Item1(decimal fee) { Fee = fee; }
public string ItemName { get { return "Item1"; } }
public virtual decimal Fee { get; set; }
}
public class TestItemJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType) { return (objectType == typeof(IItem)); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object result = null;
JObject jObj = JObject.Load(reader);
string itemTypeID = jObj["ItemName"].Value<string>();
//NOTE: My real implementation doesn't have hard coded strings or types here.
//See the code block below for actual implementation.
if (itemTypeID == "Item1")
result = jObj.ToObject(typeof(Item1), serializer);
return result;
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); }
}
class Program
{
static void Main(string[] args)
{
Test test1 = new Test(new List<Item1> { new Item1(9.00m), new Item1(24.0000m) })
{
Total = 33.0000m
};
string json = JsonConvert.SerializeObject(test1, Formatting.Indented);
Console.WriteLine(json);
Console.WriteLine();
Test test1Clone = JsonConvert.DeserializeObject<Test>(json);
string json2 = JsonConvert.SerializeObject(test1Clone, Formatting.Indented);
Console.WriteLine(json2);
Console.ReadLine();
}
}
Фрагмент моего фактического конвертера:
if (CartItemTypes.TypeMaps.ContainsKey(itemTypeID))
result = jObj.ToObject(CartItemTypes.TypeMaps[itemTypeID], serializer);