【问题标题】:Json deserialization class structure on client客户端上的Json反序列化类结构
【发布时间】:2015-04-29 23:58:31
【问题描述】:

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

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

KeepAliveMessageWebResponseMessage 的派生类。该服务返回一个IEnumerable<WebResponseMessage>

我遇到了这样的异常:

Newtonsoft.Json.JsonSerializationException:

无法将当前 JSON 对象(例如 {"name":"value"})反序列化为类型 'System.Collections.Generic.IEnumerable`1[ Red5Prototype.Models.WaitForClientMessagesResult]

因为该类型需要一个 JSON 数组(例如 [1,2,3])才能正确反序列化。

我尝试过多种方式调用反序列化:

  • WaitForClientMessagesResult deserialized = JsonConvert.DeserializeObject<WaitForClientMessagesResult>(keepAliveResult);
  • WaitForClientMessagesResult[] deserialized = JsonConvert.DeserializeObject<WaitForClientMessagesResult[]>(keepAliveResult);
  • IEnumerable<WebClientMessage> deserialized = JsonConvert.DeserializeObject<IEnumerable<WebClientMessage>>(keepAliveResult);

如果这些有效,则无。

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

编辑: 我的基类是这样定义的:

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

那也没用。

【问题讨论】:

  • 告诉我们WaitForClientMessagesResult是如何声明的。
  • 您需要使用__type 属性吗?如果是这样,这就有点复杂了
  • __type 字段来自网络服务,我无法更改。它正在使用 .NET 序列化。
  • "__type" 属性是DataContractJsonSerializer 格式化多态类型信息的方式。 Json.NET 使用完全不同的格式,which is hardcoded。您可能需要考虑切换到 DataContractJsonSerializer,因为服务器似乎正在使用它来发送数据。
  • 如果您需要继续使用 Json.NET,也许这个解决方案有效:stackoverflow.com/questions/9490345/…

标签: c# json deserialization


【解决方案1】:

这里有两个问题。首先,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 hintsDataContractJsonSerializer 格式。您正在使用的 JSON 序列化程序,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" 属性并反序列化正确的具体类型。

【讨论】:

  • 谢谢。这是有道理的。
【解决方案2】:

这就是我最终解决此问题的方法。它有点hack,但现在必须这样做:

    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 处理。如果有更好的方法,我稍后会探索。

感谢大家的帮助 :) 没有您的反馈,我无法到达这里

马特

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多