Json.NET对调用
JToken.FromObject生成“默认”序列化然后修改结果
JToken的输出的转换器不提供方便的支持-
正是因为您所观察
StackOverflowException到的递归调用
JsonConverter.WriteJson()会发生。
一种解决方法 是使用线程静态布尔值在递归调用中临时禁用转换器。使用线程静态是因为在某些情况下,包括asp.net-web-
api,JSON转换器的实例将在线程之间共享。在这种情况下,通过实例属性禁用转换器将不是线程安全的。
public class FJson : JsonConverter{ [ThreadStatic] static bool disabled; // Disables the converter in a thread-safe manner. bool Disabled { get { return disabled; } set { disabled = value; } } public override bool CanWrite { get { return !Disabled; } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { JToken t; using (new PushValue<bool>(true, () => Disabled, (canWrite) => Disabled = canWrite)) { t = JToken.FromObject(value, serializer); } if (t.Type != JTokenType.Object) { t.WriteTo(writer); return; } JObject o = (JObject)t; writer.WriteStartObject(); WriteJson(writer, o); writer.WriteEndObject(); } private void WriteJson(JsonWriter writer, JObject value) { foreach (var p in value.Properties()) { if (p.Value is JObject) WriteJson(writer, (JObject)p.Value); else p.WriteTo(writer); } } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } public override bool CanConvert(Type objectType) { return true; // works for any type }}public struct PushValue<T> : IDisposable{ Action<T> setValue; T oldValue; public PushValue(T value, Func<T> getValue, Action<T> setValue) { if (getValue == null || setValue == null) throw new ArgumentNullException(); this.setValue = setValue; this.oldValue = getValue(); setValue(value); } #region IDisposable Members // By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class. public void Dispose() { if (setValue != null) setValue(oldValue); } #endregion}
完成此 *** 作后,您可以将还原
[JsonConverter(typeof(FJson))]到您的班级
A:
[JsonConverter(typeof(FJson))]public class A{}
演示小提琴#1 在这里。
第二个更简单的变通办法 是为应用了的类型生成默认序列化,
JsonConverter它利用了一个事实,即应用于 成员的 转换器会取代应用于
类型
或设置中的转换器。从文档:
使用JsonConverter的优先级是由成员上的属性定义的JsonConverter,然后是由类中的属性定义的JsonConverter,最后是传递给JsonSerializer的所有转换器。
因此,可以通过将单个类型的成员嵌套在DTO内来为您的类型生成默认序列化,该成员的值是您的类型的实例,并且应用了虚拟转换器,除了转换为读取和读取的默认序列化之外,它什么都没做写作。
以下扩展方法和转换器可以完成此工作:
public static partial class JsonExtensions{ public static JToken DefaultFromObject(this JsonSerializer serializer, object value) { if (value == null) return JValue.CreateNull(); var dto = Activator.CreateInstance(typeof(DefaultSerializationDTO<>).MakeGenericType(value.GetType()), value); var root = JObject.FromObject(dto, serializer); return root["Value"].RemoveFromLowestPossibleParent() ?? JValue.CreateNull(); } public static object DefaultToObject(this JToken token, Type type, JsonSerializer serializer = null) { var oldParent = token.Parent; var dtoToken = new JObject(new JProperty("Value", token)); var dtoType = typeof(DefaultSerializationDTO<>).MakeGenericType(type); var dto = (IHasValue)(serializer ?? JsonSerializer.CreateDefault()).Deserialize(dtoToken.CreateReader(), dtoType); if (oldParent == null) token.RemoveFromLowestPossibleParent(); return dto == null ? null : dto.GetValue(); } public static JToken RemoveFromLowestPossibleParent(this JToken node) { if (node == null) return null; // If the parent is a JProperty, remove that instead of the token itself. var contained = node.Parent is JProperty ? node.Parent : node; contained.Remove(); // Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should if (contained is JProperty) ((JProperty)node.Parent).Value = null; return node; } interface IHasValue { object GetValue(); } [JsonObject(NamingStrategyType = typeof(DefaultNamingStrategy), IsReference = false)] class DefaultSerializationDTO<T> : IHasValue { public DefaultSerializationDTO(T value) { this.Value = value; } public DefaultSerializationDTO() { } [JsonConverter(typeof(NoConverter)), JsonProperty(ReferenceLoopHandling = ReferenceLoopHandling.Serialize)] public T Value { get; set; } object IHasValue.GetValue() { return Value; } }}public class NoConverter : JsonConverter{ // NoConverter taken from this answer https://stackoverflow.com/a/39739105/3744182 // To https://stackoverflow.com/questions/39738714/selectively-use-default-json-converter // By https://stackoverflow.com/users/3744182/dbc public override bool CanConvert(Type objectType) { throw new NotImplementedException(); } public override bool CanRead { get { return false; } } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } public override bool CanWrite { get { return false; } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); }}
然后
FJson.WriteJson()按以下方式使用它:
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer){ JToken t = serializer.DefaultFromObject(value); // Remainder as before if (t.Type != JTokenType.Object) { t.WriteTo(writer); return; } JObject o = (JObject)t; writer.WriteStartObject(); WriteJson(writer, o); writer.WriteEndObject();}
这种方法的优点是:
它不依赖于递归禁用转换器,因此可以与递归数据模型一起正常工作。
它不需要重新实现从对象属性序列化对象的整个逻辑。
演示小提琴#2 在这里。
笔记
- 两种转换器版本都只能处理写入。阅读未实现。
要在解决这个问题相当于 德 系列化,例如见 Json.NET与JsonConverter自定义序列化-
如何让“默认”的行为 。
- 您编写的转换器会创建具有重复名称的JSON:
{ "id": 1, "name": null, "name": "value"}
尽管这不是严格非法的,但通常被认为是不良做法,因此应避免使用。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)