我正在尝试找到一个困扰我几天的问题的答案目前是Silverlight。
i在服务器端具有.NET类(content1),该类别具有匹配的JSON转换器和具有类型content1属性的消息类(Message1)。这一切似乎都可以正常工作,问题是该属性被更改为键入对象(当前代码为)
public class Message1
{
public string Name { get; set; }
public object Payload { get; set; }
}
现在我的自定义jsonconverter不再被称为。
我已经将转换器放置在jsonserializer.converters Collection中,我可以看到CanConvert方法正在转换器上,但该属性是作为转换System.Object的请求。
。我已经启用了typenamehandling并将其设置为自动(所有/对象/阵列不是SignalR断开的选项),并且可以看到写入我的JSON
的$ type属性{
"Name": "Test Message 1",
"Payload": {
"$type": "Models.Content1, Models, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0d90b1aaa82178d3",
"Propert1": "This is a string"
}
}
随着跟踪的打开,我可以看到已解决的类型
已解决类型'Models.Content1,模型,版本= 1.0.0.0,culture =中性,publicKeyToken = 0D90B1AAA82178D3TO Models.Content1。路径'有效载荷。$ type'。
但是我的自定义转换器从未被调用。
所以我问题的关键是,使用$ type时,有没有办法将json.net委派给我的类级别自定义转换器?
或失败,如果我编写自定义转换器并为我的对象属性注册它
public class Message1
{
public string Name { get; set; }
[JsonConverter(typeof(YetAnotherConverter))]
public object Payload { get; set; }
}
是否有一种方法可以通过$ type属性窥视对象的类型,我真的很想这样做
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var data = JObject.Load(reader);
var type = data.Property("$type");
if (type.Value.Value<string>().Contains("Content1"))
{
var obj = serializer.Deserialize<Content1>(reader);
return obj;
}
if (type.Value.Value<string>().Contains("Content2"))
{
var obj = serializer.Deserialize<Content2>(reader);
return obj;
}
return serializer.Deserialize(reader);
}
它会为我的类型调用正确的jsonconverter,但实际上不起作用,因为jsonreader只是向前,所以我不能对此类型"窥视"。
我想我可以走JSON序列化的途径,更像
{
"Name": "Test Message 1",
"Payload": {
"$type": "Models.Content1, Models, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0d90b1aaa82178d3",
"data": {
"Propert1": "This is a string"
}
}
}
然后,我可以与读者一起通过JSON前进,获取$类型(此时可能在此时使用其他名称,因为我在原始使用中完全处理),找到数据部分,然后将其传递给具有正确对象类型的串行器,因此可以在类级别属性中调用转换器。
,老实说,这感觉就像我要沿着兔子洞走下去,这是不对的!
谢谢
史蒂芬。
在这种情况下似乎未调用多态类型的转换器。
您可以做的是创建YetAnotherConverter
;在ReadJson
中,将对象加载到JToken
中,解析"$type"
属性,然后致电JToken.ToObject(type, serializer)
将中间JToken
验证为最终类型。这样可以确保其转换器被调用。因此:
public class PolymorphicConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(object);
}
public override bool CanWrite { get { return false; } }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.Load(reader);
if (token.Type != JTokenType.Object)
return token;
var typeString = (string)token["$type"];
if (typeString == null)
return token;
string typeName, assemblyName;
SplitFullyQualifiedTypeName(typeString, out typeName, out assemblyName);
var type = serializer.Binder.BindToType(assemblyName, typeName);
if (type != null)
return token.ToObject(type, serializer);
return token;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
// Utilities taken from https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs
// I couldn't find a way to access these directly.
public static void SplitFullyQualifiedTypeName(string fullyQualifiedTypeName, out string typeName, out string assemblyName)
{
int? assemblyDelimiterIndex = GetAssemblyDelimiterIndex(fullyQualifiedTypeName);
if (assemblyDelimiterIndex != null)
{
typeName = fullyQualifiedTypeName.Substring(0, assemblyDelimiterIndex.Value).Trim();
assemblyName = fullyQualifiedTypeName.Substring(assemblyDelimiterIndex.Value + 1, fullyQualifiedTypeName.Length - assemblyDelimiterIndex.Value - 1).Trim();
}
else
{
typeName = fullyQualifiedTypeName;
assemblyName = null;
}
}
private static int? GetAssemblyDelimiterIndex(string fullyQualifiedTypeName)
{
int scope = 0;
for (int i = 0; i < fullyQualifiedTypeName.Length; i++)
{
char current = fullyQualifiedTypeName[i];
switch (current)
{
case '[':
scope++;
break;
case ']':
scope--;
break;
case ',':
if (scope == 0)
return i;
break;
}
}
return null;
}
}
,然后:
public class Message1
{
public string Name { get; set; }
[JsonConverter(typeof(PolymorphicConverter))]
public object Payload { get; set; }
}