gpt4 book ai didi

c# - 如何反序列化同一对象中具有重复属性名称的 JSON

转载 作者:太空狗 更新时间:2023-10-29 21:34:04 31 4
gpt4 key购买 nike

我有一个 JSON 字符串,我希望它包含我无法让 JSON.NET 满意的重复键。

我想知道是否有人知道最好的方法(也许使用 JsonConverter?)让 JSON.NET 更改 JObject 的子 JObjectsJArrays 当它看到重复的键名时?

// For example: This gives me a JObject with a single "JProperty\JObject" child.
var obj = JsonConvert.DeserializeObject<object>("{ \"HiThere\":1}");

// This throws:
// System.ArgumentException : Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject.
obj = JsonConvert.DeserializeObject<object>("{ \"HiThere\":1, \"HiThere\":2, \"HiThere\":3 }");

我尝试反序列化的实际 JSON 要复杂得多,并且重复项嵌套在多个级别。但是上面的代码演示了为什么它对我来说失败了。

我知道 JSON 不正确,这就是我询问 JSON.NET 是否有解决此问题的方法的原因。为了争论起见,假设我无法控制 JSON。我实际上确实为父对象使用了特定类型,但遇到问题的特定属性要么是字符串,要么是另一个嵌套的 JSON 对象。由于这个原因,失败的属性类型是“object”。

最佳答案

有趣的问题。我玩了一会儿,发现虽然 JObject 不能包含具有重复名称的属性,但在反序列化期间用于填充它的 JsonTextReader 没有这样的限制。 (如果您考虑一下,这是有道理的:它是一个只读的阅读器;它不关心过去读过的内容)。有了这些知识,我尝试编写一些代码来填充 JTokens 的层次结构,如果在特定 JObject 中遇到重复的属性名称,则根据需要将属性值转换为 JArray。由于我不知道您的实际 JSON 和要求,您可能需要对其进行一些调整,但这至少是一个开始。

代码如下:

public static JToken DeserializeAndCombineDuplicates(JsonTextReader reader)
{
if (reader.TokenType == JsonToken.None)
{
reader.Read();
}

if (reader.TokenType == JsonToken.StartObject)
{
reader.Read();
JObject obj = new JObject();
while (reader.TokenType != JsonToken.EndObject)
{
string propName = (string)reader.Value;
reader.Read();
JToken newValue = DeserializeAndCombineDuplicates(reader);

JToken existingValue = obj[propName];
if (existingValue == null)
{
obj.Add(new JProperty(propName, newValue));
}
else if (existingValue.Type == JTokenType.Array)
{
CombineWithArray((JArray)existingValue, newValue);
}
else // Convert existing non-array property value to an array
{
JProperty prop = (JProperty)existingValue.Parent;
JArray array = new JArray();
prop.Value = array;
array.Add(existingValue);
CombineWithArray(array, newValue);
}

reader.Read();
}
return obj;
}

if (reader.TokenType == JsonToken.StartArray)
{
reader.Read();
JArray array = new JArray();
while (reader.TokenType != JsonToken.EndArray)
{
array.Add(DeserializeAndCombineDuplicates(reader));
reader.Read();
}
return array;
}

return new JValue(reader.Value);
}

private static void CombineWithArray(JArray array, JToken value)
{
if (value.Type == JTokenType.Array)
{
foreach (JToken child in value.Children())
array.Add(child);
}
else
{
array.Add(value);
}
}

这是一个演示:

class Program
{
static void Main(string[] args)
{
string json = @"
{
""Foo"" : 1,
""Foo"" : [2],
""Foo"" : [3, 4],
""Bar"" : { ""X"" : [ ""A"", ""B"" ] },
""Bar"" : { ""X"" : ""C"", ""X"" : ""D"" },
}";

using (StringReader sr = new StringReader(json))
using (JsonTextReader reader = new JsonTextReader(sr))
{
JToken token = DeserializeAndCombineDuplicates(reader);
Dump(token, "");
}
}

private static void Dump(JToken token, string indent)
{
Console.Write(indent);
if (token == null)
{
Console.WriteLine("null");
return;
}
Console.Write(token.Type);

if (token is JProperty)
Console.Write(" (name=" + ((JProperty)token).Name + ")");
else if (token is JValue)
Console.Write(" (value=" + token.ToString() + ")");

Console.WriteLine();

if (token.HasValues)
foreach (JToken child in token.Children())
Dump(child, indent + " ");
}
}

输出:

Object
Property (name=Foo)
Array
Integer (value=1)
Integer (value=2)
Integer (value=3)
Integer (value=4)
Property (name=Bar)
Array
Object
Property (name=X)
Array
String (value=A)
String (value=B)
Object
Property (name=X)
Array
String (value=C)
String (value=D)

关于c# - 如何反序列化同一对象中具有重复属性名称的 JSON,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20714160/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com