我正在测试我正在处理的购物车的 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");
}
The 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 实现,但我构建了它的基本模型,但无法重现结果。最明显的区别是我的基本版本丢失了抽象基类和接口的一些额外继承/实现以及这些类和接口的一些泛型类型用法(其中泛型类型参数定义了一些嵌套子对象的类型)。
所以我希望没有人仍然可以回答:知道为什么尾随零会改变吗?反序列化任一 JSON 字符串后,这些对象似乎与原始对象相同,但我想确保 Json.NET 中没有某些内容会导致精度或舍入损失,从而可能在多次序列化后逐渐改变这些小数之一旅行。
Updated
这是一个可重现的示例。我以为我已经排除了JsonConverter
但错了。因为我的内心_items
list 是在接口上键入的,我必须告诉 Json.NET 反序列化回哪个具体类型。我不想要实际的Type
JSON 中的名称 so 而不是使用TypeNameHandling.Auto
,我给了这些项目一个唯一的字符串标识符属性。这JsonConverter
用它来选择要创建的具体类型,但我猜JObject
已经解析了我的decimal
s to 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);