将一个JSON对象层次结构反序列化为Dictionary<string, object>层次结构

4
我是在使用.NET for WinRT (C#)进行开发,我想将JSON字符串反序列化为一个Dictionary<string, object>,其中字典值稍后可以转换为实际类型。JSON字符串可以包含对象层次结构,我希望子对象也能在Dictionary<string, object>中。
以下是它应该能够处理的示例JSON:
{
  "Name":"John Smith",
  "Age":42,
  "Parent":
  {
    "Name":"Brian Smith",
    "Age":65,
    "Parent":
    {
       "Name":"James Smith",
       "Age":87,
    }
  }
}

我尝试使用DataContractJsonSerializer进行如下操作:

using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
    DataContractJsonSerializerSettings settings = new DataContractJsonSerializerSettings();
    settings.UseSimpleDictionaryFormat = true;

    DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Dictionary<string, object>), settings);
    Dictionary<string, object> results = (Dictionary<string, object>)serializer.ReadObject(ms);
}

对于第一层级,这个方法实际上可以正常工作,但是 "Parent" 只是一个无法转换为 Dictionary<string, object> 的对象:

Dictionary<string, object> parent = (Dictionary<string, object>)results["Parent"];
Cannot cast 'results["Parent"]' (which has an actual type of 'object') to 'System.Collections.Generic.Dictionary<string,object>'

我尝试使用Json.NET,但是子对象本身就是JObject,即IDictionary<string, JToken>,这迫使我必须遍历整个层次结构并再次转换它们。
有人知道如何使用现有的序列化程序解决这个问题吗?
编辑
我正在使用Dictionary<string, object>,因为我的对象从一个服务器调用到另一个服务器调用会发生变化(例如,“Id”属性可能是“id”,“cust_id”或“customerId”,具体取决于请求),而且由于我的应用程序不是唯一使用这些服务的应用程序,所以我不能改变它,至少目前还不能改变。
因此,在这种情况下,我发现使用DataContractAttributeDataMemberAttribute不方便。相反,我想将所有内容存储在通用字典中,并拥有一个单一的强类型属性“Id”,该属性查找字典中的“id”、“cust_id”或“customerId”,使其对UI透明。
这个系统与JSON.NET非常配合,但是如果服务器返回对象层次结构,则子对象将作为JObjects存储在我的字典中,而不是另一个字典中。
总之,我正在寻找一种有效的系统,可以使用WinRT中可用的JSON序列化程序将对象层次结构转换为Dictionary<string, object>层次结构。
2个回答

4

我在使用 JSON.NET 库和 ExpandoObject 类结合的方式解决 WinRT 应用程序中的同一问题。该库能够将 JSON 数据很好地反序列化为实现 IDictionary 接口的 ExpandoObjects。ExpandoObject 的键值对的值可以很容易地被视为另一个 ExpandoObject。

以下是我采用的方法,针对您的示例进行了调整:

void LoadJSONData()
{
    string testData = "{ \"Name\":\"John Smith\", \"Age\":42, \"Parent\": { \"Name\":\"Brian Smith\", \"Age\":65, \"Parent\": { \"Name\":\"James Smith\", \"Age\":87, } } }";

    ExpandoObject dataObj = JsonConvert.DeserializeObject<ExpandoObject>(testData, new ExpandoObjectConverter());

    // Grab the parent object directly (if it exists) and treat as ExpandoObject
    var parentElement = dataObj.Where(el => el.Key == "Parent").FirstOrDefault();
    if (parentElement.Value != null && parentElement.Value is ExpandoObject)
    {
        ExpandoObject parentObj = (ExpandoObject)parentElement.Value;
        // do something with the parent object...
    }

    // Alternately, iterate through the properties of the expando
    foreach (var property in (IDictionary<String, Object>)dataObj)
    {
        if (property.Key == "Parent" && property.Value != null && property.Value is ExpandoObject)
        {
            foreach (var parentProp in (ExpandoObject)property.Value)
            {
                // do something with the properties in the parent expando
            }
        }
    }
}

非常有趣,看起来就像我正在寻找的东西。我今天会尝试一下,看看它是否符合(功能和性能)! - canderso

3
尝试这段代码片段:
var d = new System.Web.Script.Serialization.JavaScriptSerializer();
var results = d.Deserialize<Dictionary<string, object>>(jsonString);
var parent = (Dictionary<string, object>)results["Parent"];

根据参考资料,JavaScriptSerializer类支持Windows 8
对于反序列化,Json.NET也是一个可以考虑的选项,它支持Windows 8补充:对于WinRT
  1. Define the DataContract according to your JSON.

    [DataContract]
    public class CustomObject
    {
        [DataMember(Name = "Name")]
        public string Name { get; set; }
    
        [DataMember(Name = "Age")]
        public string Age { get; set; }
    
        [DataMember(Name = "Parent")]
        public Dictionary<string, object> Parent { get; set; }
    }
    
  2. Use the DataContract class while deserialization.

    using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
    {
        DataContractJsonSerializerSettings settings = 
                new DataContractJsonSerializerSettings();
        settings.UseSimpleDictionaryFormat = true;
    
        DataContractJsonSerializer serializer = 
                new DataContractJsonSerializer(typeof(CustomObject), settings);
    
        CustomObject results = (CustomObject)serializer.ReadObject(ms);
        Dictionary<string, object> parent = results.Parent;
    }
    

参考资料:使用DataContractJsonSerializer从JSON创建.NET对象


请注意,类型包含在System.Web.Extensions程序集中。它是否可用于WinRT应用程序? - fsimonazzi
根据我目前的研究结果...是的,http://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptserializer.aspx 和 http://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptserializer.aspx。 - Furqan Safdar
嗯,它未显示在受支持的API列表中http://msdn.microsoft.com/en-us/library/windows/apps/br230232.aspx - fsimonazzi
1
.NET在Windows 8主机上运行并不等同于WinRT .NET应用程序。 - fsimonazzi
谢谢,但System.Web命名空间在.NET for WinRT中不可用。关于第二种解决方案,在这种情况下它可以工作,因为层次结构的所有对象都是相同的,但在我的真实应用程序中并非总是如此(顺便提一下,这就是使用Dictionary<string,object>的原因)。 - canderso
DataContractJsonSerializer 是构建用于与 DataContract 一起使用的,因此如果您采用此方法,则必须指定 DataContract,否则尝试使用 数据契约代理 定义自己的自定义反序列化技术,参见 http://msdn.microsoft.com/en-us/library/ms733064.aspx。 - Furqan Safdar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接