【问题标题】:Trouble reading a nested structure读取嵌套结构时遇到问题
【发布时间】:2020-01-06 17:44:27
【问题描述】:

我正在尝试使用 Unity 的 JSON 实用程序读取 JSON 文件,该文件如下所示:

{ "entries": [{
    "2019": [{
        "january": [{
            "6": [{
                "name": "Litago",
                "ingredients": [{
                    "kaloriar": "20",
                    "salt": "10"
                }]
            }]
        }]
    }]
}]
}

我对如何设置嵌套类有点苦恼。我目前正在这样做,但它不起作用。

[System.Serializable]
public class Entries
{
    public KeyValuePair<string, List<Year>> Year;
}

[System.Serializable]
public class Year
{
    public KeyValuePair<string, List<Month>> Month;
}

[System.Serializable]
public class Month
{
    public KeyValuePair<string, List<Day>> Day;
}

[System.Serializable]
public class Day
{
    public KeyValuePair<string, List<Meal>> Meal;
}

[System.Serializable]
public class Meal
{
    public string Name;
    public List<KeyValuePair<string, string>> ingredients;
}

我是这样读取 JSON 的:

Entries entries = JsonUtility.FromJson<Entries>(JSONString);

理想情况下,我想做这样的事情:

Debug.Log(entries["2019"]["January"]["6"]["name"]); // Should print "Litago"

但由于我的课程很可能没有正确设置,我会收到类型错误。任何想法都将不胜感激,欢迎提出其他更好的插件来读取 JSON 的建议!

【问题讨论】:

  • 你能发布你得到的错误吗?当你做点符号时会发生什么? entry.2019.January.6.name
  • JsonUtility 大部分都很好(tm),但如果你想要另一个插件,Json Dot Net。
  • 不知道如何使用 JsonUtility 执行此操作,但使用 json.net 您可以使用字典列表和 Meal 对象列表反序列化您的 JSON,例如public class RootObject { public List&lt;Dictionary&lt;string, List&lt;Dictionary&lt;string, List&lt;Dictionary&lt;string, List&lt;Meal&gt;&gt;&gt;&gt;&gt;&gt;&gt; Entries { get; set; } }。请参阅:dotnetfiddle.net/ld5BaB。这是否回答你的问题?老实说,JSON 有点尴尬,你有机会简化格式吗?
  • 无论您使用哪种序列化程序,我确定您不想要KeyValuePair&lt;string, List&lt;T&gt;&gt;,您会想要一个字典列表。
  • AFAIK Unity 的 JsonUtility 无法直接处理嵌套类型、列表、字典、KeyValuePairs 等。来自文档 - Passing other types directly to the API, for example primitive types or arrays, is not currently supported. For now you will need to wrap such types in a class or struct of some sort.。 Newtonsoft 可以以更健壮和直观的方式处理这样的 JSON,我建议改用它。

标签: c# json unity3d


【解决方案1】:

要回答您的问题,即如何访问数据,您只需执行以下操作即可访问您需要的内容。

   JObject entries = JObject.Parse(jsonString);
   Console.WriteLine(entries["entries"][0]["2019"][0]["january"][0]["6"][0]["ingredients"][0]["kaloriar"].ToString());

输出

20

创建字典字典...

您可以创建一个递归方法来构建您的 Dictionary&lt;string, object&gt; 项目。它必须是对象字典的原因是因为每次进入子节点时都有动态值。

    public static Dictionary<string, object> BuildDictionary(JObject input)
    {
        var properties = input.Properties();

        // Terminator
        if (properties.ToList().Where(x => x.Name.Equals("name")).Count() > 0)
        {
            Day thisDay = new Day()
            {
                name = input["name"].ToString(),
                ingredients = new Ingredients()
                {
                    kaloriar = input["ingredients"][0]["kaloriar"].ToString(),
                    salt = input["ingredients"][0]["salt"].ToString()
                }
            };
            return new Dictionary<string, object>() { { "Meal", thisDay } };
        }

        // Recursive
        Dictionary<string, object> obj = new Dictionary<string, object>();
        foreach (JProperty property in properties) 
        {
            foreach (var element in input[property.Name])
                obj.Add(property.Name, BuildDictionary(element as JObject));

        }
        return obj;
    }

主要用途

   JObject entries = JObject.Parse(jsonString);
   Dictionary<string, object> dict = BuildDictionary(entries);

结果字典

{
  "entries": {
    "2019": {
      "january": {
        "6": {
          "Meal": {
            "name": "Litago",
            "ingredients": {
              "kaloriar": "20",
              "salt": "10"
            }
          }
        }
      }
    }
  }
}

您可以访问与您正在寻找的数据非常相似的数据。

Console.WriteLine(JObject.Parse(JsonConvert.SerializeObject(dict))["entries"]["2019"]["january"]["6"]["Meal"]["ingredients"]["kaloriar"].ToString());

输出

20

本质上,您所做的是获取元素数组并将元素仅转换为字典,以便以您想要的方式访问。

【讨论】:

    【解决方案2】:

    如果您愿意恢复到 Json.Net 库,您可以利用它的可扩展点来构建与您想要的构造非常相似的东西:

    1. List&lt;T&gt; 上覆盖 [] 运算符,以便允许字符串输入并使链接看起来更自然。

    2. 覆盖 Json.Net 附带的 ExpandoObjectConverter,以便注入您的自定义列表而不是默认列表。

    总体而言,代码可能如下所示:

    public class SearchableList<T> : List<T>
    {
        public object this[string item]
        {
            get { 
                var listItem = this.Cast<IDictionary<string, object>>().First(l => l.ContainsKey(item)); // I am assuming that your top level array items will only have one matching key
                return listItem[item];
            }
        }
    }
    public class MyConverter : ExpandoObjectConverter
    {
        static bool IsPrimitiveToken(JsonToken token)
        {
            if ((uint)(token - 7) <= 5u || (uint)(token - 16) <= 1u)
            {
                return true;
            }
            return false;
        }
    
        bool MoveToContent(JsonReader reader)
        {
            JsonToken tokenType = reader.TokenType;
            while (tokenType == JsonToken.None || tokenType == JsonToken.Comment)
            {
                if (!reader.Read())
                {
                    return false;
                }
                tokenType = reader.TokenType;
            }
            return true;
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            return ReadValue(reader);
        }
    
        private object ReadValue(JsonReader reader)
        {
            if (!MoveToContent(reader))
            {
                throw new JsonSerializationException("Unexpected end when reading ExpandoObject.");
            }
            switch (reader.TokenType)
            {
                case JsonToken.StartObject:
                    return ReadObject(reader);
                case JsonToken.StartArray:
                    return ReadList(reader);
                default:
                    if (IsPrimitiveToken(reader.TokenType))
                    {
                        return reader.Value;
                    }
                    throw new JsonSerializationException("Unexpected token when converting ExpandoObject");
            }
        }
    
        private object ReadList(JsonReader reader)
        {
            IList<object> list = new SearchableList<object>(); // it is quite unfortunate to have to reimplement all class just because of this one line.
            while (reader.Read())
            {
                switch (reader.TokenType)
                {
                    case JsonToken.EndArray:
                        return list;
                    case JsonToken.Comment:
                        continue;
                }
                object item = ReadValue(reader);
                list.Add(item);
            }
            throw new JsonSerializationException("Unexpected end when reading ExpandoObject.");
        }
    
        private object ReadObject(JsonReader reader)
        {
            IDictionary<string, object> dictionary = new ExpandoObject();
            while (reader.Read())
            {
                switch (reader.TokenType)
                {
                    case JsonToken.PropertyName:
                        {
                            string key = reader.Value.ToString();
                            if (!reader.Read())
                            {
                                throw new JsonSerializationException("Unexpected end when reading ExpandoObject.");
                            }                       
                            object obj2 = dictionary[key] = ReadValue(reader);
                            break;
                        }
                    case JsonToken.EndObject:
                        return dictionary;
                }
            }
            throw new JsonSerializationException("Unexpected end when reading ExpandoObject.");
        }
    }
    void Main()
    {
        var myConverter = new MyConverter();
        dynamic entries = JsonConvert.DeserializeObject<ExpandoObject>("your json here", myConverter);
        Console.WriteLine(entries.entries["2019"]["january"]["6"]["name"]);
    }
    

    您会注意到,MyConverter 有很多看似无关的代码,这有点不幸,因为 ExpandoObjectConverter 开箱即用的可扩展性非常有限。您可能只使用股票标准ExpandoObjectConverter,但鉴于您的源 json 格式,它产生的对象遍历起来有点尴尬。

    希望这能给你一个探索的途径。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-05-26
      • 1970-01-01
      • 2014-08-31
      • 2014-05-07
      • 2017-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多