JSON Deserialize 中的一个数据作为数组,另一个作为 JSON 对象传入的棘手节点

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

JSON Deserialize in one data as array and in one as json object coming tricky node

问题

以下是您要翻译的内容:

这是JSON格式。

{
  "variants": [
    {
      "name": "test1",
      "description": "test1",
      "images": []
    },
    {
      "name": "test2",
      "description": "test2",
      "images": {
        "WHITE": [
          {
            "id": "ttest",
            "product_id": "ttest"
          }
        ]
      }
    }
  ]
}

现在,如您所见,images 中的一个是数组,另一个是JSON对象。

我尝试过以下方法。

public List<Variant> variants { get; set; }

public class Variant
{
    public string name { get; set; }
    public string description { get; set; }
    public List<Image> images { get; set; }
}

public class Image
{
    public List<White> White { get; set; }
}

public class White
{
    public string id { get; set; }
    public string product_id { get; set; }
}

然后使用以下代码进行反序列化:

var items = JsonConvert.DeserializeObject<TestClass>(content);
英文:

I have following json, I am trying to Deserialize it. Tricky part is that in one node with name images in some data it is coming as array and in some it is coming as json object.. so how I can Deserialize both at a time.

This is JSON format.

{
  &quot;variants&quot;: [
    {
      &quot;name&quot;: &quot;test1&quot;,
      &quot;description&quot;: &quot;test1&quot;,
      &quot;images&quot;: []
    },
    {
      &quot;name&quot;: &quot;test2&quot;,
      &quot;description&quot;: &quot;test2&quot;,
      &quot;images&quot;: {
        &quot;WHITE&quot;: [
          {
            &quot;id&quot;: &quot;ttest&quot;,
            &quot;product_id&quot;: &quot;ttest&quot;
          }
        ]
      }
    }
  ]
}

Now as you see here in images in one we have an array and in one we have json object.

I have tried below.

public List&lt;Variant&gt; variants { get; set; }

public class Variant
    {
        public string name { get; set;}
        public string description { get; set;}
        public List&lt;Image&gt; images { get; set; }
    }

public class Image
    {
        public List&lt;White&gt; White { get; set; }
    }

public class White
    {
        public string id { get; set;}
        public string product_id { get; set;}
    }

Then below line to Deserialize:


var items = JsonConvert.DeserializeObject&lt;TestClass&gt;(content);

答案1

得分: 1

以下是翻译的内容:

`images` 的值似乎是:

 - 一个对应于任意颜色图像数据列表的字典对象,在这里是 `&quot;WHITE&quot;`。
 - 当没有颜色数值时,是一个空数组。

由于 `images` 的值有时是数组,有时是对象,您将需要使用适当的 [自定义 `JsonConverter&lt;T&gt;`](https://www.newtonsoft.com/json/help/html/CustomJsonConverterGeneric.htm) 进行反序列化。

具体来说,定义以下数据模型:

	public class TestClass
    {
        public List&lt;Variant&gt; variants { get; set; } = new ();
    }

    public class Variant
    {
        public string name { get; set; }
        public string description { get; set; }
		
		[JsonConverter(typeof(JsonSingleOrEmptyArrayConverter&lt;Dictionary&lt;string, List&lt;Image&gt;&gt;&gt;))]
		public Dictionary&lt;string, List&lt;Image&gt;&gt; images { get; set; } = new ();
    }

    public class Image
    {
        public string id { get; set; }
        public string product_id { get; set; }
    }

其中 `JsonSingleOrEmptyArrayConverter&lt;T&gt;` 是从 [此答案](https://stackoverflow.com/a/53219877) 复制而来,地址为 *https://stackoverflow.com/q/53219668/3744182*:

	public class JsonSingleOrEmptyArrayConverter&lt;T&gt; : JsonConverter where T : class
	{
		// 从这个答案 https://stackoverflow.com/a/53219877 复制过来的
		// 到 https://stackoverflow.com/questions/53219668/cannot-deserialize-the-current-json-object-empty-array
		public override bool CanConvert(Type objectType)
		{
			return typeof(T).IsAssignableFrom(objectType);
		}

		public override bool CanWrite { get { return false; } }

		public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
		{
			var contract = serializer.ContractResolver.ResolveContract(objectType);
			// 允许字典合同和对象合同,因为两者都由以 {(左括号)开始并以 }(右括号)结束的无序名称/值对组成。
			if (!(contract is Newtonsoft.Json.Serialization.JsonObjectContract 
				  || contract is Newtonsoft.Json.Serialization.JsonDictionaryContract))
			{
				throw new JsonSerializationException(string.Format(&quot;不支持的 objectType {0} 在 {1}。&quot;, objectType, reader.Path));
			}

			switch (reader.SkipComments().TokenType)
			{
				case JsonToken.StartArray:
					{
						int count = 0;
						while (reader.Read())
						{
							switch (reader.TokenType)
							{
								case JsonToken.Comment:
									break;
								case JsonToken.EndArray:
									// 如果 existingValue 为 null,您可能希望在此处分配一个空对象
									// 如果是这样,请使用
									// return existingValue ?? contract.DefaultCreator();
									return existingValue;
								default:
									{
										count++;
										if (count &gt; 1)
											throw new JsonSerializationException(string.Format(&quot;在路径 {0} 处有太多对象。&quot;, reader.Path));
										existingValue = existingValue ?? contract.DefaultCreator();
										serializer.Populate(reader, existingValue);
									}
									break;
							}
						}
						// 不应该走到这里。
						throw new JsonSerializationException(string.Format(&quot;在路径 {0} 处有未关闭的数组。&quot;, reader.Path));
					}

				case JsonToken.Null:
					return null;

				case JsonToken.StartObject:
					existingValue = existingValue ?? contract.DefaultCreator();
					serializer.Populate(reader, existingValue);
					return existingValue;

				default:
					throw new InvalidOperationException(&quot;意外的令牌类型 &quot; + reader.TokenType.ToString());
			}
		}

		public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
		{
			throw new NotImplementedException();
		}
	}

	public static partial class JsonExtensions
	{
		public static JsonReader SkipComments(this JsonReader reader)
		{
			while (reader.TokenType == JsonToken.Comment &amp;&amp; reader.Read())
				;
			return reader;
		}
	}

现在您将能够通过简单地执行以下代码来反序列化您的 JSON:

	var root = JsonConvert.DeserializeObject&lt;TestClass&gt;(json);

演示示例在 [此处](https://dotnetfiddle.net/OWRR9O)。

英文:

The value for images seems to be either:

  • An object corresponding to a dictionary of lists of image data for arbitrary colors, here &quot;WHITE&quot;.
  • An empty array when there are no colors values.

Since the value of images is sometimes an array and sometimes an object, you will need to deserialize it using an appropriate custom JsonConverter&lt;T&gt;.

Specifically, define the following data model:

public class TestClass
{
    public List&lt;Variant&gt; variants { get; set; } = new ();
}

public class Variant
{
    public string name { get; set; }
    public string description { get; set; }
	
	[JsonConverter(typeof(JsonSingleOrEmptyArrayConverter&lt;Dictionary&lt;string, List&lt;Image&gt;&gt;&gt;))]
	public Dictionary&lt;string, List&lt;Image&gt;&gt; images { get; set; } = new ();
}

public class Image
{
    public string id { get; set; }
    public string product_id { get; set; }
}

Where JsonSingleOrEmptyArrayConverter&lt;T&gt; is copied verbatim from this answer to https://stackoverflow.com/q/53219668/3744182:

public class JsonSingleOrEmptyArrayConverter&lt;T&gt; : JsonConverter where T : class
{
	// Copied from this answer https://stackoverflow.com/a/53219877
	// To https://stackoverflow.com/questions/53219668/cannot-deserialize-the-current-json-object-empty-array
	public override bool CanConvert(Type objectType)
	{
		return typeof(T).IsAssignableFrom(objectType);
	}

	public override bool CanWrite { get { return false; } }

	public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
	{
		var contract = serializer.ContractResolver.ResolveContract(objectType);
		// Allow for dictionary contracts as well as objects contracts, since both are represented by 
		// an unordered set of name/value pairs that begins with { (left brace) and ends with } (right brace).
		if (!(contract is Newtonsoft.Json.Serialization.JsonObjectContract 
			  || contract is Newtonsoft.Json.Serialization.JsonDictionaryContract))
		{
			throw new JsonSerializationException(string.Format(&quot;Unsupported objectType {0} at {1}.&quot;, objectType, reader.Path));
		}

		switch (reader.SkipComments().TokenType)
		{
			case JsonToken.StartArray:
				{
					int count = 0;
					while (reader.Read())
					{
						switch (reader.TokenType)
						{
							case JsonToken.Comment:
								break;
							case JsonToken.EndArray:
								// You might want to allocate an empty object here if existingValue is null
								// If so, do
								// return existingValue ?? contract.DefaultCreator();
								return existingValue;
							default:
								{
									count++;
									if (count &gt; 1)
										throw new JsonSerializationException(string.Format(&quot;Too many objects at path {0}.&quot;, reader.Path));
									existingValue = existingValue ?? contract.DefaultCreator();
									serializer.Populate(reader, existingValue);
								}
								break;
						}
					}
					// Should not come here.
					throw new JsonSerializationException(string.Format(&quot;Unclosed array at path {0}.&quot;, reader.Path));
				}

			case JsonToken.Null:
				return null;

			case JsonToken.StartObject:
				existingValue = existingValue ?? contract.DefaultCreator();
				serializer.Populate(reader, existingValue);
				return existingValue;

			default:
				throw new InvalidOperationException(&quot;Unexpected token type &quot; + reader.TokenType.ToString());
		}
	}

	public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
	{
		throw new NotImplementedException();
	}
}

public static partial class JsonExtensions
{
	public static JsonReader SkipComments(this JsonReader reader)
	{
		while (reader.TokenType == JsonToken.Comment &amp;&amp; reader.Read())
			;
		return reader;
	}
}

And now you will be able to deserialize your JSON by simply doing:

var root = JsonConvert.DeserializeObject&lt;TestClass&gt;(json);

Demo fiddle here.

huangapple
  • 本文由 发表于 2023年5月18日 05:39:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/76276372.html
匿名

发表评论

匿名网友

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

确定