JsonConverters和对象属性

时间:2015-05-11 18:26:27

标签: json.net

我正在试图找到一个让我困扰了几天的问题的答案,我正在将一些传统的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(此时应该使用不同的名称,因为我在原始使用中已完全损坏),找到数据部分然后将其传递给具有正确对象类型的序列化程序,因此它将在类级别属性中调用转换器。

但老实说,我觉得自己要像兔子洞那么深,这不可能是对的!

由于

斯蒂芬。

1 个答案:

答案 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; }
}