同一模式下具有相同名称的两个属性。尝试检索属性值时出错

时间:2014-08-27 21:35:54

标签: c# json json.net

我试图以递归方式运行Json文件并检索名为" fileName"的属性,然后将该属性的值添加到ListView。但问题是,正如标题所说,同一模式中有两个相同属性的实例,这就是我认为导致错误的原因。

我想忽略" fileName"包含" spigot.jar"的属性,仅检索包含" spigot-1.7.10-R0.1-SNAPSHOT.jar"的属性。

我试图解析的Json示例(或使用http://ci.md-5.net/job/Spigot/api/json?depth=1作为参考):

"artifacts" : [
    {
      "displayPath" : "spigot-1.7.10-R0.1-SNAPSHOT.jar",
      "fileName" : "spigot-1.7.10-R0.1-SNAPSHOT.jar",
      "relativePath" : "Spigot-Server/target/spigot-1.7.10-R0.1-SNAPSHOT.jar"
    },
    {
      "displayPath" : "spigot.jar",
      "fileName" : "spigot.jar",
      "relativePath" : "Spigot-Server/target/spigot.jar"
    }
  ]


我如何尝试解析,并将其添加到C#中的ListView:

var url = "http://ci.md-5.net/job/Spigot/api/json?depth=1";
var content = (new WebClient()).DownloadString(url);

dynamic json = JsonConvert.DeserializeObject(content);

foreach (var builds in json.builds)
{
    string fileName = builds.artifacts.fileName;

    lvServers.Items.Add(fileName);
}


我应该如何检索" fileName"财产成功吗?

3 个答案:

答案 0 :(得分:4)

看起来工件是一个数组,所以你需要迭代它们或通过索引器访问:

foreach (var artifact in builds.artifacts)
{
    var fileName = artifact.fileName;
}

var fileName = builds.artifacts[0].fileName;

答案 1 :(得分:3)

尝试以下方法。这将列出构建号加上每个构建的第一个文件名:

var url = "http://ci.md-5.net/job/Spigot/api/json?depth=1";
var content = (new WebClient()).DownloadString(url);

JObject root = JObject.Parse(content);

var list = root["builds"].Select(b => 
           b["number"].ToString() + " - " + 
           b["artifacts"].Select(a => a["fileName"].ToString())
                         .FirstOrDefault());

foreach (var fileName in list)
{
    lvServers.Items.Add(fileName);
}

演示https://dotnetfiddle.net/54l8YX

请注意,构建#1603没有工件(我猜测因为该构建的结果是FAILURE)所以文件名为空。


解释上述代码中的内容

我使用Json.Net的LINQ-to-JSON API(JObjects,JTokens,JArrays等)与System.Linq命名空间中的内置.NET扩展方法相结合({ {3}},Select)和几个FirstOrDefault从JSON层次结构中提取数据。

以下是它如何分解:

  1. 我首先使用JObject将下载的内容解析为JObject.Parse(content)

  2. 使用JObject,我可以使用方括号语法(就像Dictionary一样)来获取该对象中的直接子属性的值。 root["builds"]为我提供JArray JObjects代表构建列表。

  3. Select方法允许我获取某些内容的lambda expressions(在这种情况下代表构建的JArray JObjects),迭代该列表并将一个函数应用于列表中的每个项目,以便将其转换为其他内容的列表(在本例中为字符串列表)。

  4. 我应用于每个版本JObject的功能是IEnumerableb => b["number"] + " - " + b["artifacts"] ...。在这个表达式中,我说#34;来自构建b,将number属性作为字符串,将其与分隔符-连接,并使用获得第一个的子表达式构建工件列表中的文件名。

  5. 在子表达式中,我得到构建的artifacts属性的值(JArray的另一个JObjects),然后使用{{1}使用lambda表达式Select将其转换为文件名列表。但是,因为我只想要第一个fileName,所以我使用a => a[fileName].ToString()来剔除列表到单个项目(如果没有项目,则为null)。

  6. 希望这有点道理。如果您不熟悉LINQ或lambda表达式,那么代码肯定会有点神秘。下面是一个不使用这些结构的替代版本,但完全相同。这可能会更容易理解。

    FirstOrDefault()

    演示lambda expression

答案 2 :(得分:0)

强大地将转换JSON键入C#对象将获得编译时错误。因为您使用的是动态关键字,所以在尝试运行应用程序之前不会出现编译器错误。

创建这些类: Classes

然后将您当前的代码修改为:

var url = "http://ci.md-5.net/job/Spigot/api/json?depth=1";
var content = (new WebClient()).DownloadString(url);

var responseObj = JsonConvert.DeserializeObject<RootObject>(content);

因为它们现在是强类型的,所以你将能够得到编译器错误,以向你显示你做错了。尽量远离动态对象。

现在出现错误,因为您没有正确使用它:

foreach (var builds in json.builds)
{
    string fileName = builds.artifacts.fileName;

    lvServers.Items.Add(fileName);
}

您的代码现在会产生这样的错误通知您,它不对:

'System.Collections.Generic.IEnumerable<UserQuery.Artifact>' does not contain a definition for 'fileName' and no extension method 'fileName' accepting a first argument of type 'System.Collections.Generic.IEnumerable<UserQuery.Artifact>' could be found (press F4 to add a using directive or assembly reference)

如果您需要生成将来的课程,请使用工具JSON2CSHARP。只知道你需要清除重复的东西,但这很可怕。