同一架构下的两个同名属性.尝试检索属性的值时出错

本文关键字:属性 出错 检索 一架 两个 | 更新日期: 2023-09-27 18:19:36

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

我想忽略包含"spigot-1.7.10-R0.1-SNAPSHOT.jar"的"fileName"属性,只检索包含"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"属性?

同一架构下的两个同名属性.尝试检索属性的值时出错

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

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

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

尝试以下操作。这将列出每个版本的版本号和第一个文件名:

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命名空间(SelectFirstOrDefault)的内置.Net扩展方法和两个lambda表达式从Json层次结构中提取数据。

以下是它的分解方式:

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

  2. 对于JObject,我可以使用方括号语法(类似于Dictionary)来获取该对象中直接子属性的值。CCD_ 7给我一个CCD_ 9的CCD_。

  3. Select方法允许我获取某个事物的IEnumerable(在本例中为JObjectsJArray,表示构建),对该列表进行迭代,并对列表中的每个项应用一个函数,以便将其转换为其他事物的列表(在本案中为字符串列表)。

  4. 我应用于每个构建JObject的函数是lambda表达式:b => b["number"] + " - " + b["artifacts"] ...。在这个表达式中,我说"从构建b中,将number属性作为字符串,用分隔符-和子表达式连接,子表达式从构建的工件列表中获得第一个文件名。

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

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

var url = "http://ci.md-5.net/job/Spigot/api/json?depth=1";
var content = (new WebClient()).DownloadString(url);
JObject root = JObject.Parse(content);
foreach (JObject build in root["builds"])
{
    string buildName = build["number"].ToString() + " - ";
    foreach (JObject artifact in build["artifacts"])
    {
        JToken fileName = artifact["fileName"];
        if (fileName != null)
        {
            buildName += fileName.ToString();   
        }
        break;
    }
    lvServers.Items.Add(buildName);
}

演示:https://dotnetfiddle.net/vwebrY

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

创建这些类:类

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

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。只要知道你需要剔除重复的内容,这太可怕了。