我正在试图找到一个让我困扰了几天的问题的答案,我正在将一些传统的WCF代码转换为SignalR,它是一个内部API,客户端现在是Silverlight。
我在服务器端有一个.NET类(Content1),它有一个匹配的JSON转换器和一个具有Content1类型属性的消息类(Message1)。这一切似乎工作正常,问题是当该属性更改为类型对象(当前代码是)
public class Message1
{
public string Name { get; set; }
public object Payload { get; set; }
}
现在我的自定义JsonConverter不再被调用。
我已将转换器放在JsonSerializer.Converters集合中,我可以看到转换器上的CanConvert方法被命中,但该属性是作为转换System.Object的请求而来的。
我启用了typeNameHandling并将其设置为Auto(All / Object / Array不是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,Models,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = 0d90b1aaa82178d3to Models.Content1。路径'有效载荷。$ type'。
但我的自定义转换器永远不会被调用。
所以我的问题的症结在于,有没有办法让Json.Net在使用$ type时委托给我的班级自定义转换器?
如果我写了一个自定义转换器并将其注册为我的对象属性
,那么就失败了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);
}
哪个会为我的类型调用正确的JsonConverters,但实际上并不起作用,因为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与阅读器前进,获得$ type(此时应该使用不同的名称,因为我在原始使用中已完全损坏),找到数据部分然后将其传递给具有正确对象类型的序列化程序,因此它将在类级别属性中调用转换器。
但老实说,我觉得自己要像兔子洞那么深,这不可能是对的!
由于
斯蒂芬。
答案 0 :(得分:0)
在这种情况下,看起来不会调用多态类型的转换器。
您可以做的是创建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; }
}