JSON.net(de)序列化无类型属性

假设我有一个这样的类:

public class Example { public int TypedProperty { get; set; } public object UntypedProperty { get; set; } } 

假设某人出现并写道:

 var example = new Example { TypedProperty = 5, UntypedProperty = Guid.NewGuid() } 

如果我用JsonConvert.SerializeObject(example)序列化它,我得到

 { "TypedProperty": 5, "UntypedProperty": "24bd733f-2ade-4374-9db6-3c9f3d97b12c" } 

理想情况下,我想得到这样的东西:

 { "TypedProperty": 5, "UntypedProperty": { "$type": "System.Guid,mscorlib", "$value": "24bd733f-2ade-4374-9db6-3c9f3d97b12c" } } 

TypeNameHandling在此方案中不起作用。 我怎样才能(de)序列化一个无类型的属性?

如果使用TypeNameHandling.AllTypeNameHandling.Auto序列化类,那么当UntypedProperty属性被序列化为JSON容器(对象或数组)时,Json.NET应该通过在JSON文件中存储类型信息来正确地序列化和反序列化它在"$type"属性中。 但是,如果将UntypedProperty序列化为JSON原语(字符串,数字或布尔值),则这不起作用,因为正如您所指出的,JSON原语没有机会包含"$type"属性。

当使用类型为object的属性序列化类型时,解决方案是按照此答案的行序列化可以封装类型信息的原始值的包装类。 这是一个自定义JSON转换器 ,它注入了这样一个包装器:

 public class UntypedToTypedValueConverter : JsonConverter { public override bool CanConvert(Type objectType) { throw new NotImplementedException("This converter should only be applied directly via ItemConverterType, not added to JsonSerializer.Converters"); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; var value = serializer.Deserialize(reader, objectType); if (value is TypeWrapper) { return ((TypeWrapper)value).ObjectValue; } return value; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { if (serializer.TypeNameHandling == TypeNameHandling.None) { Console.WriteLine("ObjectItemConverter used when serializer.TypeNameHandling == TypeNameHandling.None"); serializer.Serialize(writer, value); } // Handle a couple of simple primitive cases where a type wrapper is not needed else if (value is string) { writer.WriteValue((string)value); } else if (value is bool) { writer.WriteValue((bool)value); } else { var contract = serializer.ContractResolver.ResolveContract(value.GetType()); if (contract is JsonPrimitiveContract) { var wrapper = TypeWrapper.CreateWrapper(value); serializer.Serialize(writer, wrapper, typeof(object)); } else { serializer.Serialize(writer, value); } } } } abstract class TypeWrapper { protected TypeWrapper() { } [JsonIgnore] public abstract object ObjectValue { get; } public static TypeWrapper CreateWrapper(T value) { if (value == null) return new TypeWrapper(); var type = value.GetType(); if (type == typeof(T)) return new TypeWrapper(value); // Return actual type of subclass return (TypeWrapper)Activator.CreateInstance(typeof(TypeWrapper<>).MakeGenericType(type), value); } } sealed class TypeWrapper : TypeWrapper { public TypeWrapper() : base() { } public TypeWrapper(T value) : base() { this.Value = value; } public override object ObjectValue { get { return Value; } } public T Value { get; set; } } 

然后使用[JsonConverter(typeof(UntypedToTypedValueConverter))]将其应用于您的类型:

 public class Example { public int TypedProperty { get; set; } [JsonConverter(typeof(UntypedToTypedValueConverter))] public object UntypedProperty { get; set; } } 

如果你不能以任何方式修改Example类来添加这个属性(你的注释该类不是我的改变建议那么多)你可以使用自定义合约解析器注入转换器 :

 public class UntypedToTypedPropertyContractResolver : DefaultContractResolver { readonly UntypedToTypedValueConverter converter = new UntypedToTypedValueConverter(); // As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons. // http://www.newtonsoft.com/json/help/html/ContractResolver.htm // http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm // "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance." // See also https://stackoverflow.com/questions/33557737/does-json-net-cache-types-serialization-information static UntypedToTypedPropertyContractResolver instance; // Explicit static constructor to tell C# compiler not to mark type as beforefieldinit static UntypedToTypedPropertyContractResolver() { instance = new UntypedToTypedPropertyContractResolver(); } public static UntypedToTypedPropertyContractResolver Instance { get { return instance; } } protected override JsonObjectContract CreateObjectContract(Type objectType) { var contract = base.CreateObjectContract(objectType); foreach (var property in contract.Properties.Concat(contract.CreatorParameters)) { if (property.PropertyType == typeof(object) && property.Converter == null) { property.Converter = property.MemberConverter = converter; } } return contract; } } 

并使用如下:

 var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, ContractResolver = UntypedToTypedPropertyContractResolver.Instance, }; var json = JsonConvert.SerializeObject(example, Formatting.Indented, settings); var example2 = JsonConvert.DeserializeObject(json, settings); 

在这两种情况下,创建的JSON看起来像:

 { "TypedProperty": 5, "UntypedProperty": { "$type": "Question38777588.TypeWrapper`1[[System.Guid, mscorlib]], Tile", "Value": "e2983c59-5ec4-41cc-b3fe-34d9d0a97f22" } } 

查找SerializeWithJsonConverters.htm和ReadingWritingJSON 。 调用: JsonConvert.SerializeObject(example, new ObjectConverter());

 class ObjectConverter : JsonConverter { public override bool CanConvert(Type objectType) { return objectType == typeof(Example); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { Example e = (Example)value; writer.WriteStartObject(); writer.WritePropertyName("TypedProperty"); writer.WriteValue(e.TypedProperty); writer.WritePropertyName("UntypedProperty"); writer.WriteStartObject(); writer.WritePropertyName("$type"); writer.WriteValue(e.UntypedProperty.GetType().FullName); writer.WritePropertyName("$value"); writer.WriteValue(e.UntypedProperty.ToString()); writer.WriteEndObject(); writer.WriteEndObject(); } }