问题迭代通过JSON文件生成的动态对象

时间:2016-05-02 17:25:34

标签: c# json loops dictionary javascriptserializer

我有一个json文件(为了这个问题)我简化了:

{
  "servername": {
    "goodvolumes": [
      {
        "Name": "vol1",
        "State": "online",
        "Size": "12.0 TB"
      },
      {
        "Name": "vol2",
        "State": "online",
        "Size": "10.0 TB"
      }
    ],
    "BadVolumes": {
      "Name": "badVol",
      "State": "offline",
      "TotalSize": "120GB"
    }
  }
}

当读入我的C#时,我有一个System.Collections.Generic.Dictionary<string,object>类型的数据对象。

然后我遍历对象并创建一个模型对象,我将要传递给我的视图。

这给我带来了困难。这是我如何迭代json的一个例子。

//top level of my JSON - the serverName
foreach(serverName in jsonData) 
{
    //record the serverName

    //second level of my JSON - the notification name
    foreach(notification in serverName.Value)
    {
        //record the notification name

        //3rd Level of my JSON - iterating the entries
        foreach(entry in notification.Value)
        {
            //Iterating all the values in an entry
            foreach(entryValue in entry)
            {
               //record values in each entry
            }

        }
    }
}

我遇到的问题是,如果只有一个条目,则迭代第三级。

根据JSON的性质,如果通知类型有多个条目,那么在我的notifications.Value内部将会有另一个集合列表。在这种情况下,我的代码就像一个魅力。

但是,如果通知只有一个条目,notification.value实际上包含单个条目中所有值的KeyValuePair列表。所以第三级迭代不起作用。它实际上是试图在那时迭代值。

不幸的是,我正在使用的json无法修改,这是我将要接收它的格式。

我希望这能准确地解释我所遇到的问题。我知道问题出在哪里,我根本不确定如何绕过它。

1 个答案:

答案 0 :(得分:2)

首先,您可以考虑切换到。如果您这样做,可以使用How to handle both a single item and an array for the same property using JSON.net中的Dictionary<string, Dictionary<string, List<Dictionary<string, string>>>>直接反序列化为SingleOrArrayConverter<Dictionary<string, string>>。这也避免了JavaScriptSerializer使用的proprietary date format

使用JavaScriptSerializer,您需要了解它如何反序列化任意JSON数据的一些细节。具体来说,它将JSON对象反序列化为IDictionary<string, object>,将JSON数组反序列为某种非词典,非string IEnumerable。以下扩展方法实现了这些检查:

public static class JavaScriptSerializerObjectExtensions
{
    public static bool IsJsonArray(this object obj)
    {
        if (obj is string || obj.IsJsonObject())
            return false;
        return obj is IEnumerable;
    }

    public static IEnumerable<object> AsJsonArray(this object obj)
    {
        if (obj is string || obj.IsJsonObject())
            return null;
        return (obj as IEnumerable).Cast<object>();
    }

    public static bool IsJsonObject(this object obj)
    {
        return obj is IDictionary<string, object>;
    }

    public static IDictionary<string, object> AsJsonObject(this object obj)
    {
        return obj as IDictionary<string, object>;
    }

    /// <summary>
    /// If the incoming object corresponds to a JSON array, return it.  Otherwise wrap it in an array.
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    public static IEnumerable<object> ToJsonArray(this object obj)
    {
        if (obj.IsJsonArray())
            return obj.AsJsonArray();
        return new[] { obj };
    }

    public static string JsonPrimitiveToString(this object obj, bool isoDateFormat = true)
    {
        if (obj == null)
            return null; // Or return "null" if you prefer.
        else if (obj is string)
            return (string)obj;
        else if (obj.IsJsonArray() || obj.IsJsonObject())
            return new JavaScriptSerializer().Serialize(obj);
        else if (isoDateFormat && obj is DateTime)
            // Return in ISO 8601 format not idiosyncratic JavaScriptSerializer format
            // https://stackoverflow.com/questions/17301229/deserialize-iso-8601-date-time-string-to-c-sharp-datetime
            // https://msdn.microsoft.com/en-us/library/az4se3k1.aspx#Roundtrip
            return ((DateTime)obj).ToString("o");
        else
        {
            var s = new JavaScriptSerializer().Serialize(obj);
            if (s.Length > 1 && s.StartsWith("\"", StringComparison.Ordinal) && s.EndsWith("\"", StringComparison.Ordinal))
                s = s.Substring(1, s.Length - 2);
            return s;
        }
    }
}

然后你可以按如下方式反序列化:

var jsonData = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(jsonString);

Dictionary<string, Dictionary<string, List<Dictionary<string, string>>>> finalData;

// I could have just done var finalData = ... here.  I declared finalData explicitly to makes its type explicit.
finalData =
    jsonData.ToDictionary(
    p1 => p1.Key,
    p1 => p1.Value
        .AsJsonObject()
        .ToDictionary(
            p2 => p2.Key,
            p2 => (p2.Value.ToJsonArray().Select(a => a.AsJsonObject())).Select(o => o.ToDictionary(p3 => p3.Key, p3 => p3.Value.JsonPrimitiveToString())).ToList()
        ));

既然您拥有完全类型的词典层次结构,那么您应该能够继续创建最终模型。