Json 客户端上的反序列化 class 结构

Json deserialization class structure on client

我无法反序列化客户端端点上的 JSON 数据。它接收 JSON 这样的数据:

{
    "WaitForClientMessagesResult": [
        {
            "__type": "KeepAliveMessage:#Data.WebGateway",
            "MessageId": 1,
            "Type": 0,
            "PositionInQueue": -1
        }
    ]
}

KeepAliveMessageWebResponseMessage 的派生 class。服务 returns 和 IEnumerable<WebResponseMessage>.

我遇到这样的异常:

Newtonsoft.Json.JsonSerializationException:

Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.IEnumerable`1[ Red5Prototype.Models.WaitForClientMessagesResult]

because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.

我试过很多方法调用反序列化:

None 如果这些有效。

我不确定如何在客户端构建我的 classes 以使用 Json 反序列化器。

编辑: 我的基础 class 是这样定义的:

[KnownType(typeof(KeepAliveMessage))]    
[DataContract]
public abstract class WebClientMessage
{
    public WebClientMessage() { }

    [DataMember]
    public int MessageId { get; set; }
    [DataMember]
    public WebClientMessageType Type { get; set; }
}

像这样使用 Keepalive:

[DataContract]
public class KeepAliveMessage : WebClientMessage
{
    public KeepAliveMessage() { }

    [DataMember]
    public int PositionInQueue { get; set; }
}

我试着让 WebClientMessage 成为 WaitForClientMessagesResult

的成员
[DataContract]
public class WaitForClientMessagesResult
{
    public WaitForClientMessagesResult() {}

    [DataMember]
    WebClientMessage [] Messages;
}

那也没用。

这里有两个问题。首先,JSON 根对象有一个名为 WaitForClientMessagesResult 的数组值 属性 而不是 Messages 所以你需要做这样的事情:

[DataContract(Name = "WaitForClientMessagesResult", Namespace = "http://schemas.datacontract.org/2004/07/Data.WebGateway")]
public class WaitForClientMessagesResult
{
    public WaitForClientMessagesResult() { }

    [DataMember(Name = "WaitForClientMessagesResult")]
    public WebClientMessage[] Messages { get; set; }
}

其次,您的 JSON 包含 polymorphic type hints in the DataContractJsonSerializer format. The JSON serializer you are using, Json.NET, does not support this format。因此,您可以考虑切换到 DataContractJsonSerializer。使用它我能够反序列化你的 JSON 如下:

public enum WebClientMessageType
{
    KeepAliveMessage,
}

[KnownType(typeof(KeepAliveMessage))]
[DataContract(Name="WebClientMessage", Namespace="http://schemas.datacontract.org/2004/07/Data.WebGateway")]
public abstract class WebClientMessage
{
    public WebClientMessage() { }

    [DataMember]
    public int MessageId { get; set; }

    [DataMember]
    public WebClientMessageType Type { get; set; }
}

[DataContract(Name = "KeepAliveMessage", Namespace = "http://schemas.datacontract.org/2004/07/Data.WebGateway")]
public class KeepAliveMessage : WebClientMessage
{
    public KeepAliveMessage() { }

    [DataMember]
    public int PositionInQueue { get; set; }
}

public static class DataContractJsonSerializerHelper
{
    public static string GetJson<T>(T obj, DataContractJsonSerializer serializer)
    {
        using (var memory = new MemoryStream())
        {
            serializer.WriteObject(memory, obj);
            memory.Seek(0, SeekOrigin.Begin);
            using (var reader = new StreamReader(memory))
            {
                return reader.ReadToEnd();
            }
        }
    }

    public static string GetJson<T>(T obj)
    {
        var serializer = new DataContractJsonSerializer(typeof(T));
        return GetJson(obj, serializer);
    }

    public static T GetObject<T>(string json, DataContractJsonSerializer serializer)
    {
        using (var stream = GenerateStreamFromString(json))
        {
            var obj = serializer.ReadObject(stream);
            return (T)obj;
        }
    }

    public static T GetObject<T>(string json)
    {
        var serializer = new DataContractJsonSerializer(typeof(T));
        return GetObject<T>(json, serializer);
    }

    private static MemoryStream GenerateStreamFromString(string value)
    {
        return new MemoryStream(Encoding.Unicode.GetBytes(value ?? ""));
    }
}

然后,进行测试:

    public static void Test()
    {
        // Note there cannot be a space between the "{" and the "_type": 
        string json = @"{
            ""WaitForClientMessagesResult"": [
                {""__type"": ""KeepAliveMessage:#Data.WebGateway"",
                    ""MessageId"": 1,
                    ""Type"": 0,
                    ""PositionInQueue"": -1
                }
            ]
        }";
        var result = DataContractJsonSerializerHelper.GetObject<WaitForClientMessagesResult>(json);
        var newJson = DataContractJsonSerializerHelper.GetJson(result);

        Debug.Assert(JToken.DeepEquals(JToken.Parse(json), JToken.Parse(newJson))); // No assert
    }

如果您想坚持使用 Json.NET,您将需要编写自己的 JsonConverter 来解析 "__type" 属性 并反序列化正确的具体类型。

这是我最终解决这个问题的方法。这有点麻烦,但现在必须这样做:

    Dictionary<string, object> deserialized = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
    JArray messagesArray = deserialized["WaitForClientMessagesResult"] as JArray;

    foreach (JObject gatewayMessage in messagesArray.Children<JObject>())
    {
        string messageJson = gatewayMessage.ToString();
        WebClientMessageType messageType = GetMessageType(gatewayMessage);
        WaitForClientMessagesResult msg = null;

        switch (messageType)
        {
            case WebClientMessageType.KeepAlive:
                msg = JsonConvert.DeserializeObject<KeepAliveMessage>(messageJson);
                break;
            default:
                break;
        }

    }

我去掉了其他业务逻辑,只发布了 Json 处理。如果有更好的方法,我稍后再探讨。

谢谢大家的帮助 :) 没有你们的反馈我无法来到这里

马特