什么是解析动态命名的JSON对象的最有效方法?

5

我正在尝试解析一个JSON响应,其中包括一些我不太熟悉的内容,也没有经常看到。

在一个JSON对象内部,有一个动态命名的JSON对象。

在这个例子中,在"bugs"里面有一个名为"12345"的JSON对象,与一个缺陷号相关联。

{
   "bugs" : {
      "12345" : {
         "comments" : [
            {
               "id" : 1,
               "text" : "Description 1"
            },
            {
               "id" : 2,
               "text" : "Description 2"
            }
         ]
      }
   }
}

我感兴趣的是:如何最有效地解析这样一个动态命名的JSON对象?
给定一些类似于JSON工具,比如: 它们将把上面的JSON响应转换成以下形式的类:
public class Comment
{
    public int id { get; set; }
    public string text { get; set; }
}

public class 12345
{
    public IList<Comment> comments { get; set; }
}

public class Bugs
{
    public 12345 12345 { get; set; }
}

public class Root
{
    public Bugs bugs { get; set; }
}

json2charp

public class Comment
{
    public int id { get; set; }
    public string text { get; set; }
}

public class __invalid_type__12345
{
    public List<Comment> comments { get; set; }
}

public class Bugs
{
    public __invalid_type__12345 __invalid_name__12345 { get; set; }
}

public class RootObject
{
    public Bugs bugs { get; set; }
}

这个问题的关键在于它生成了一个动态名称的class。因此,对此API使用其他标识符进行后续查询会导致失败,因为名称不匹配,而且生成的[JsonProperty("")]也会包含上面生成示例中的动态类名。
虽然JSON有效,但这似乎是以这种方式格式化的JSON的限制。不幸的是,我无法控制这个JSON API,所以我想知道解决这个问题的最佳方法是什么?

“12345” 不是一个有效的类名,如果您无法控制属性名称,这可能会成为一种重大问题。 - Dan Wilson
@DanWilson 对的,这就是为什么我想知道在没有对后备存储器进行控制的情况下如何处理动态JSON对象。这似乎是糟糕的JSON实践的边缘案例? - Jon Douglas
是的,保持属性名称静态并为它们赋予动态值是有帮助的。@michael-gunter的答案可能是解决方案。 - Dan Wilson
Firebase以这种格式输出数组。请参阅此Firebase博客了解详情。他们甚至说数组是邪恶的。真的。 - Vanquished Wombat
2个回答

4
请尝试使用Json.NET进行翻译,可作为Nuget软件包(Newtonsoft.Json)或通过http://www.newtonsoft.com/json获取。
Json.NET可以执行基于类的序列化/反序列化,就像您所展示的那样。对于Json格式未知或在开发时不固定的情况,它还提供了通用的JObject和JToken类。
以下是从文件加载json对象的示例。
// load file into a JObject
JObject document;
using (var fileStream = File.OpenRead(someFilePath))
using (var streamReader = new StreamReader(fileStream))
using (var jsonReader = new JsonTextReader(streamReader))
    document = JObject.Load(jsonReader);

// read the JObject
var bugs = (JObject) document["bugs"];
foreach (var bugEntry in bugs)
{
    var bugID = bugEntry.Key;
    var bugData = (JObject) bugEntry.Value;
    var comments = (JArray) bugData["comments"];
    foreach (JObject comment in comments)
        Debug.Print(comment["text"]);
}

使用正确的支持模型,JsonConvert.DeserializeObject<T>不能完成这个任务吗?这似乎是一种非常手动的方法,而不是允许后备模型定义约束条件。感谢您的快速回答! - Jon Douglas
我有点困惑,@JonDouglas。拥有一个后备模型似乎与拥有动态属性名称相矛盾。你要么提前知道属性名称,要么就不知道。 - Dan Wilson
我在考虑,也许一个“转换器”可以解析像这样的动态命名对象,并将其添加到支持模型中。我还尝试使用“ExpandoObject”来实现这个目的,但似乎这只是一个我需要手动处理的限制。 - Jon Douglas
仔细看,似乎这个对象并不是具有属性的对象,而是键值对字典。JSON 不区分它们,但正如 @JonDouglas 所发现的那样,你消费它们的方式是不同的。我倾向于认为 .NET 的大多数 JSON 库都支持字典属性(如 Eugene 的答案所述),因此如果你想要严格的类定义,请尝试使用它。如果需要更灵活的内容,则可以尝试使用像 JObject 这样的通用工具。 - Michael Gunter
谢谢你的回答,Michael。我发现Eugene更新的答案最接近我想要的。 - Jon Douglas

3

Newtonsoft.Json JsonConvert 可以将其解析为一个 Dictionary<String, Comments>,只要提供相应的模型类:

public class Comment
{
    public int id { get; set; }
    public string text { get; set; }
}

public class Comments
{
    public List<Comment> comments { get; set; }
}

public class RootObject
{
    public Dictionary<String, Comments> bugs { get; set; }
}

可以通过以下方式进行检查:

var json = "{\r\n   \"bugs\" : {\r\n      \"12345\" : {\r\n         \"comments\" : [\r\n            {\r\n               \"id\" : 1,\r\n               \"text\" : \"Description 1\"\r\n            },\r\n            {\r\n               \"id\" : 2,\r\n               \"text\" : \"Description 2\"\r\n            }\r\n         ]\r\n      }\r\n   }\r\n}";

Console.WriteLine(json);

var obj = JsonConvert.DeserializeObject<RootObject>(json);

Console.WriteLine(obj.bugs["12345"].comments.First().text);

我将此标记为答案,因为这是最直接的方法,而无需手动解压缩JSON。感谢您的时间和回答! - Jon Douglas
1
字典也可以与JavaScriptSerializer一起使用,以及与UseSimpleDictionaryFormat = trueDataContractJsonSerializer一起使用。 - dbc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接