json.net 将派生序列化为通用基础
json.net serialize derived as generic base
(这个问题源于尝试 serialize/deserialize LikeType 类 到 JSON - https://github.com/kleinwareio/LikeType)
我有:
public abstract class LikeType<T>
{
public T Value;
// ....
// how to tell json.net to serialize/deserialize classes deriving
// from this like it would T ???
}
public class Name : LikeType<string> {
public Name(string s) : base(s) { }
// does not add any properties
}
void test()
{
var name = new Name("john");
var jobj = new JObject();
try
{
jobj.Add("key", new JObject(name));
}
catch (Exception e)
{
!Exeption !
e = {System.ArgumentException: Could not determine JSON object type for type Name. at Newtonsoft.Json.Linq.JValue.GetValueType(Nullable`1 current, Object value) at Newtonsoft.Json.Linq.JContainer.CreateFromContent(Object content)
}
}
我如何指定从 LikeType<T>
派生的所有 类 将序列化/反序列化为 JSON 和 Json.Net 以与 T
相同的方式?
(在这种情况下,Json.Net 应该 serialize/deserialize 以与字符串相同的方式命名)
我相信您想要 "forward" LikeType<T>
序列化,将其视为不可见的包装类型。这个假设对我的解决方案至关重要。
我建议使用 JsonConverter
实现来做到这一点。这里有个很像的post:
我已根据您的情况修改了示例。这是适应的方法:
class LikeTypeConverter : JsonConverter
{
static Type GetValueType(Type objectType)
{
return objectType
.BaseTypesAndSelf()
.Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(LikeType<>))
.Select(t => t.GetGenericArguments()[0])
.FirstOrDefault();
}
public override bool CanConvert(Type objectType)
{
return GetValueType(objectType) != null;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// You need to decide whether a null JSON token results in a null LikeType<T> or
// an allocated LikeType<T> with a null Value.
if (reader.SkipComments().TokenType == JsonToken.Null)
return null;
var valueType = GetValueType(objectType);
var value = serializer.Deserialize(reader, valueType);
// Here we assume that every subclass of LikeType<T> has a constructor with a single argument, of type T.
return Activator.CreateInstance(objectType, value);
}
const string ValuePropertyName = "Value";// nameof(LikeType<object>.Value); // in C#6+
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(value.GetType());
var valueProperty = contract.Properties.Single(p => p.UnderlyingName == ValuePropertyName);
serializer.Serialize(writer, valueProperty.ValueProvider.GetValue(value));
}
}
public static partial class JsonExtensions
{
public static JsonReader SkipComments(this JsonReader reader)
{
while (reader.TokenType == JsonToken.Comment && reader.Read())
{
}
return reader;
}
}
public static class TypeExtensions
{
public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
{
while (type != null)
{
yield return type;
type = type.BaseType;
}
}
}
如果您想将其包含在您的库中,您可以将其用作 LikeType<T>
声明的属性:
[JsonConverter(typeof(LikeTypeConverter))]
public abstract class LikeType<T> { ... }
或者你可以在必要的时候使用转换器,修改JsonSerializerSettings.Converters
集合:
var settings = new JsonSerializerSettings
{
Converters = { new LikeTypeConverter() },
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
var result = JsonConvert.SerializeObject(myObject, Formatting.Indented, settings);
我还创建了一个 working dotnetfiddle sample 用于演示(还改编了链接 post 中的那个)。
控制大多数序列化器序列化的内容的一种方法是使用 Serializable attribute and implement the ISerializable interface。
(这个问题源于尝试 serialize/deserialize LikeType 类 到 JSON - https://github.com/kleinwareio/LikeType)
我有:
public abstract class LikeType<T>
{
public T Value;
// ....
// how to tell json.net to serialize/deserialize classes deriving
// from this like it would T ???
}
public class Name : LikeType<string> {
public Name(string s) : base(s) { }
// does not add any properties
}
void test()
{
var name = new Name("john");
var jobj = new JObject();
try
{
jobj.Add("key", new JObject(name));
}
catch (Exception e)
{
!Exeption !
e = {System.ArgumentException: Could not determine JSON object type for type Name. at Newtonsoft.Json.Linq.JValue.GetValueType(Nullable`1 current, Object value) at Newtonsoft.Json.Linq.JContainer.CreateFromContent(Object content)
}
}
我如何指定从 LikeType<T>
派生的所有 类 将序列化/反序列化为 JSON 和 Json.Net 以与 T
相同的方式?
(在这种情况下,Json.Net 应该 serialize/deserialize 以与字符串相同的方式命名)
我相信您想要 "forward" LikeType<T>
序列化,将其视为不可见的包装类型。这个假设对我的解决方案至关重要。
我建议使用 JsonConverter
实现来做到这一点。这里有个很像的post:
我已根据您的情况修改了示例。这是适应的方法:
class LikeTypeConverter : JsonConverter
{
static Type GetValueType(Type objectType)
{
return objectType
.BaseTypesAndSelf()
.Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(LikeType<>))
.Select(t => t.GetGenericArguments()[0])
.FirstOrDefault();
}
public override bool CanConvert(Type objectType)
{
return GetValueType(objectType) != null;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// You need to decide whether a null JSON token results in a null LikeType<T> or
// an allocated LikeType<T> with a null Value.
if (reader.SkipComments().TokenType == JsonToken.Null)
return null;
var valueType = GetValueType(objectType);
var value = serializer.Deserialize(reader, valueType);
// Here we assume that every subclass of LikeType<T> has a constructor with a single argument, of type T.
return Activator.CreateInstance(objectType, value);
}
const string ValuePropertyName = "Value";// nameof(LikeType<object>.Value); // in C#6+
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(value.GetType());
var valueProperty = contract.Properties.Single(p => p.UnderlyingName == ValuePropertyName);
serializer.Serialize(writer, valueProperty.ValueProvider.GetValue(value));
}
}
public static partial class JsonExtensions
{
public static JsonReader SkipComments(this JsonReader reader)
{
while (reader.TokenType == JsonToken.Comment && reader.Read())
{
}
return reader;
}
}
public static class TypeExtensions
{
public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
{
while (type != null)
{
yield return type;
type = type.BaseType;
}
}
}
如果您想将其包含在您的库中,您可以将其用作 LikeType<T>
声明的属性:
[JsonConverter(typeof(LikeTypeConverter))]
public abstract class LikeType<T> { ... }
或者你可以在必要的时候使用转换器,修改JsonSerializerSettings.Converters
集合:
var settings = new JsonSerializerSettings
{
Converters = { new LikeTypeConverter() },
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
var result = JsonConvert.SerializeObject(myObject, Formatting.Indented, settings);
我还创建了一个 working dotnetfiddle sample 用于演示(还改编了链接 post 中的那个)。
控制大多数序列化器序列化的内容的一种方法是使用 Serializable attribute and implement the ISerializable interface。