读取嵌套结构时出现问题。

huangapple go评论83阅读模式
英文:

Trouble reading a nested structure

问题

我正在尝试使用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"]); // 应该打印出 "Litago"

但由于我的类设置可能不正确,我收到了类型错误。如果有任何建议,将不胜感激,并欢迎其他更好的插件来读取JSON!

英文:

I'm trying to read a JSON file using Unity's JSON utility, and the file looks like this:

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

I'm struggling a bit with how I should set up my nested classes. I'm currently doing this, but it's not working.

[System.Serializable]
public class Entries
{
    public KeyValuePair&lt;string, List&lt;Year&gt;&gt; Year;
}

[System.Serializable]
public class Year
{
    public KeyValuePair&lt;string, List&lt;Month&gt;&gt; Month;
}

[System.Serializable]
public class Month
{
    public KeyValuePair&lt;string, List&lt;Day&gt;&gt; Day;
}

[System.Serializable]
public class Day
{
    public KeyValuePair&lt;string, List&lt;Meal&gt;&gt; Meal;
}

[System.Serializable]
public class Meal
{
    public string Name;
    public List&lt;KeyValuePair&lt;string, string&gt;&gt; ingredients;
}

I read the JSON like so:

Entries entries = JsonUtility.FromJson&lt;Entries&gt;(JSONString);

Ideally, I would like to do something like:

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

but as my classes are most likely not setup correctly, I get type errors. Any ideas would be appreciated, and suggestions for other better plugins to read the JSON is welcome!

答案1

得分: 1

Here is the translated code portion you requested:

要回答你的问题,如何访问数据,你可以简单地执行以下操作以访问所需内容。

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

输出

20

创建字典的字典...

你可以创建一个递归方法来构建你的Dictionary<string, object>项目。之所以要是对象字典是因为每次进入子节点时都有动态值。

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

    // 终止条件
    if (properties.ToList().Any(x => x.Name.Equals("name")))
    {
        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 } };
    }

    // 递归
    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

本质上,你所做的是将元素数组转换为字典,以便以你想要的方式进行访问。


请注意,代码中的引号是直接翻译的,你可能需要根据你的编程环境进行必要的修正。

<details>
<summary>英文:</summary>

To answer your question, how to access the data, you can simply do the following to access what you need.

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


**Output**

20


**Create Dictionary of Dictionary...**

You can create a recursive method to build out your `Dictionary&lt;string, object&gt;` items. Reason it has to be Dictionary of objects is because you have dynamic values each time you go in sub node.

public static Dictionary&lt;string, object&gt; BuildDictionary(JObject input)
{
    var properties = input.Properties();

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

    // Recursive
    Dictionary&lt;string, object&gt; obj = new Dictionary&lt;string, object&gt;();
    foreach (JProperty property in properties) 
    {
        foreach (var element in input[property.Name])
            obj.Add(property.Name, BuildDictionary(element as JObject));
            
    }
    return obj;
}
**Usage in Main**

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


**Resulting Dictionary**

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


And you can access the data you are looking for very similarly to what you wre looking for.

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

**Output**

20


In essence what you are doing is taking the array of elements and converting only elements into dictionaries for accessing the way you want.

</details>



# 答案2
**得分**: 1

以下是翻译的内容:

如果您愿意回到[Json.Net](https://www.newtonsoft.com/json)库,您可以利用其可扩展性点来构建与您所需的结构非常相似的东西:

1. 重写`List<T>`上的`[]`操作符,以便允许字符串输入,并使链接看起来更自然。
2. 重写Json.Net附带的`ExpandoObjectConverter`,以便注入您的自定义列表而不是默认值。

总的来说,代码可能如下所示:

```csharp
public class SearchableList<T> : List<T>
{
    public object this[string item]
    {
        get { 
            var listItem = this.Cast<IDictionary<string, object>>().First(l => l.ContainsKey(item)); // 我假设您的顶级数组项只会有一个匹配的键
            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("在读取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("在转换ExpandoObject时出现意外令牌。");
        }
    }

    private object ReadList(JsonReader reader)
    {
        IList<object> list = new SearchableList<object>(); // 不幸的是,由于这一行,不得不重新实现所有类有点不太幸运。
        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("在读取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("在读取ExpandoObject时出现意外结束。");
                        }						
                        object obj2 = dictionary[key] = ReadValue(reader);
                        break;
                    }
                case JsonToken.EndObject:
                    return dictionary;
            }
        }
        throw a JsonSerializationException("在读取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在默认情况下的可扩展性相对有限所导致的不太幸运的后果。在您的源JSON格式下,您可能只需使用标准的ExpandoObjectConverter,但它生成的对象在遍历时可能会变得有点尴尬。

希望这为您提供了一个探索的途径。

英文:

If you are open to reverting to Json.Net library, you could leverage it's extensibility points to build something very similar to your desired construct:

  1. Override [] operator on a List&lt;T&gt; so it allows for string input and make chaining look more natural.
  2. Override ExpandoObjectConverter that comes with Json.Net so it injects your custom lists instead of default.

Overall the code might look something like this:

public class SearchableList&lt;T&gt; : List&lt;T&gt;
{
	public object this[string item]
	{
		get { 
			var listItem = this.Cast&lt;IDictionary&lt;string, object&gt;&gt;().First(l =&gt; 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) &lt;= 5u || (uint)(token - 16) &lt;= 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(&quot;Unexpected end when reading ExpandoObject.&quot;);
		}
		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(&quot;Unexpected token when converting ExpandoObject&quot;);
		}
	}

	private object ReadList(JsonReader reader)
	{
		IList&lt;object&gt; list = new SearchableList&lt;object&gt;(); // 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(&quot;Unexpected end when reading ExpandoObject.&quot;);
	}

	private object ReadObject(JsonReader reader)
	{
		IDictionary&lt;string, object&gt; dictionary = new ExpandoObject();
		while (reader.Read())
		{
			switch (reader.TokenType)
			{
				case JsonToken.PropertyName:
					{
						string key = reader.Value.ToString();
						if (!reader.Read())
						{
							throw new JsonSerializationException(&quot;Unexpected end when reading ExpandoObject.&quot;);
						}						
						object obj2 = dictionary[key] = ReadValue(reader);
						break;
					}
				case JsonToken.EndObject:
					return dictionary;
			}
		}
		throw new JsonSerializationException(&quot;Unexpected end when reading ExpandoObject.&quot;);
	}
}
void Main()
{
	var myConverter = new MyConverter();
	dynamic entries = JsonConvert.DeserializeObject&lt;ExpandoObject&gt;(&quot;your json here&quot;, myConverter);
	Console.WriteLine(entries.entries[&quot;2019&quot;][&quot;january&quot;][&quot;6&quot;][&quot;name&quot;]);
}

you will notice, MyConverter has a lot of seemingly unrelated code, which is a bit unfortunate consequence of how ExpandoObjectConverter has pretty limited extensibility out of the box. You could potentially do just with stock standard ExpandoObjectConverter but the object it produces gets a bit awkward to traverse given your source json format.

Hopefully this gives you an avenue to explore.

huangapple
  • 本文由 发表于 2020年1月7日 01:44:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/59616629.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定