JsonConverters和对象属性 [英] JsonConverters and object properties

查看:75
本文介绍了JsonConverters和对象属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找一个困扰我几天的问题的答案,我正在将一些旧的WCF代码转换为SignalR,这是一个内部API,目前客户端是Silverlight.

I'm trying to find an answer to a problem that has stumped me for a few days, I'm converting some legacy WCF code over to SignalR, it's an internal API and the client is Silverlight for the moment.

我在服务器端有一个.NET类(Content1),该类具有匹配的JSON转换器和一个消息类(Message1),该消息类具有Content1类型的属性.这一切似乎都可以正常工作,问题是当该属性更改为类型object(当前代码为)时

I have a .NET class (Content1) on the server side that has a matching JSON converter and a message class (Message1) that has a property of type Content1. This all seems to work fine, the problem is when that property is changed to type object (which the current code is)

public class Message1
{
    public string Name { get; set; }

    public object Payload { get; set; }
}

现在不再调用我的自定义JsonConverter.

Now my custom JsonConverter is no longer called.

我已将转换器放置在JsonSerializer.Converters集合中,并且可以看到CanConvert方法在转换器上被命中,但是该属性是作为转换System.Object的请求而提供的.

I've placed the converter in the JsonSerializer.Converters collection and I can see the CanConvert method being hit on the converter, but the property comes through as a request to convert System.Object.

我已启用typeNameHandling并将其设置为Auto(SignalR中断时,All/Object/Array不是一个选项),并且可以看到$ type属性被写入了我的JSON

I've enabled typeNameHandling and set it to Auto (All/Object/Array are not an option as SignalR breaks), and can see the $type property being written into my JSON

{
  "Name": "Test Message 1",
  "Payload": {
    "$type": "Models.Content1, Models, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0d90b1aaa82178d3",
    "Propert1": "This is a string"
  }
}

并且打开了跟踪,我可以看到正在解析的类型

and with tracing turned on I can see the type being resolved

解析的类型'Models.Content1,模型,版本= 1.0.0.0,区域性=中性,PublicKeyToken = 0d90b1aaa82178d3至Models.Content1.路径有效载荷.$ type".

Resolved type 'Models.Content1, Models, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0d90b1aaa82178d3to Models.Content1. Path 'Payload.$type'.

但是我的自定义转换器从未被调用.

But my custom converter is never called.

所以我问题的症结在于,使用$ type时是否有办法让Json.Net委托给我的类级自定义转换器?

或者失败,如果我编写了一个自定义转换器并将其注册为我的对象属性

Or failing that, if I write a custom converter and register it for my object property

public class Message1
{
    public string Name { get; set; }

    [JsonConverter(typeof(YetAnotherConverter))]
    public object Payload { get; set; }
}

有没有一种方法可以通过$ type属性来窥视对象的类型,我真的很想这样做

Is there a way to peek ahead at the type of the object via the $type property, I'd really like to do this

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只是转发,所以我不能'窥视'该类型.

Which would invoke the correct JsonConverters for my type, but in reality does not work as JsonReader is forward only so I can't 'peek' at the type.

我想我可以走下使json序列化的路线,如下所示

I suppose I could go the route of making the json serialise more like below

{
  "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(这时可能已经使用了另一个名称,因为我已经完全破坏了原始用途),找到了data部分,然后将其传递给具有正确对象类型的序列化程序,以便它将在类级别属性中调用转换器.

Then I can edge forward through the JSON with the reader, get the $type (should probably use a different name at this point as I've completely mangled in original use), find the data section and then pass that off to the serialiser with the correct object type so it would invoke the converter in the class level attribute.

但是,老实说,这感觉就像我要钻进一个兔子洞那么深,这是不对的!

But this honestly feels like I'm heading of down a rabbit hole so deep, it can't be right!

谢谢

斯蒂芬.

推荐答案

在这种情况下,未调用多态类型的转换器.

Looks like the converter for the polymorphic type is not called in this case.

您可以做的是创建您的YetAnotherConverter;在ReadJson中将对象加载到JToken中,解析"$type"属性,然后调用

What you can do instead is to create your YetAnotherConverter; in ReadJson load the object into a JToken, parse the "$type" property, then call JToken.ToObject(type, serializer) to deserialize the intermediate JToken to your final type. This ensures its converter gets called. Thus:

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

这篇关于JsonConverters和对象属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆