如何实现json的自定义序列化,将类型附加到属性名称?

我正在尝试使用newtonsoft.json将对象序列化为JSON。 唯一的事情是我不能将json类型附加到字段名称。 考虑这个例子:

var item = new { value = "value", data = new []{"str", "str"}, b = true }; 

我想将其转换为

 { "value.string" : "value", "data.array" : ["str", "str"], "b.bool" : true } 

或类似的东西。 我们的想法是将json类型(不是c#类型)附加到json字段。 我不想附加C#类型的原因是因为它可能很复杂(有时类型是匿名的,有时它是IEnumerable,等等)

我见过许多可以转换为C#类型的解决方案,例如实现IContractResolver。 不幸的是,这不适用于这种情况。

我也不知道我之前会转换的类型。

我能找到的最接近的是

 public JObject Convert(JObject data) { var queue = new Queue(); foreach (var child in data.Children()) { queue.Enqueue(child); } while (queue.Count > 0) { var token = queue.Dequeue(); if (token is JProperty p) { if (p.Value.Type != JTokenType.Object) { token.Replace(new JProperty( $"{p.Name}.{p.Value.Type}", p.Value )); } } foreach (var child in token.Children()) { queue.Enqueue(child); } } return data; } 

但它不适用于嵌套对象

 var result = convertor.Convert(JObject.FromObject(new { nested = new { item = "str"}})); 

出于某种原因,Replace不适用于嵌套对象。 不确定它是不是一个bug。

您的主要问题是,当您将子JToken添加到父级,并且该子级已经拥有父级时,将克隆该子级并将克隆添加到父级 – 在本例中为新的JProperty 。 然后,当您使用new属性替换原始属性时,克隆的值层次结构将替换整个JToken树中的原始值层次结构。 最后,当你这样做

 foreach (var child in token.Children()) { queue.Enqueue(child); } 

您最终会循环遍历已经克隆和替换的原始子项。 虽然属性值是基元时无关紧要,但如果值是数组或其他容器,则会导致出现问题。

(次要的,潜在的问题是你没有处理根容器是一个数组的可能性。)

修复是为了防止在将属性值添加到新属性之前从旧属性中删除属性值来批量克隆属性值,然后循环遍历新属性的子属:

 public static class JsonExtensions { public static TJToken Convert(this TJToken data) where TJToken : JToken { var queue = new Queue(); foreach (var child in data.Children()) { queue.Enqueue(child); } while (queue.Count > 0) { var token = queue.Dequeue(); if (token is JProperty) { var p = (JProperty)token; if (p.Value.Type != JTokenType.Object) { var value = p.Value; // Remove the value from its parent before adding it to a new parent, // to prevent cloning. p.Value = null; var replacement = new JProperty( string.Format("{0}.{1}", p.Name, value.Type), value ); token.Replace(replacement); token = replacement; } } foreach (var child in token.Children()) { queue.Enqueue(child); } } return data; } } 

工作.Net小提琴 。

为什么Json.NET在将其添加到新JProperty时克隆该值? 发生这种情况是因为JToken层次结构中的父项和子项之间存在双向引用:

  • JToken.Children()遍历给定标记的所有子标记;

  • JToken.Parent获取给定令牌的父级。

因此, JToken不能有两个父母 – 也就是说,它不能同时存在于JToken层次结构中的两个位置。 因此,当您将属性值添加到新的JProperty ,前一个父级应该会发生什么? 可能性包括:

  1. 前一个父级未经修改,子级的克隆将添加到新父级。

  2. 通过将子项替换为其子项的克隆来修改前一个父项。

  3. 通过使用null JValue替换子项来修改前一个父JValue

事实certificate,Json.NET采用选项#1,导致您的错误。