我正试图递归地运行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命名空间(Select
、FirstOrDefault
)的内置.Net扩展方法和两个lambda表达式从Json层次结构中提取数据。
以下是它的分解方式:
我首先使用
JObject.Parse(content)
将下载的内容解析为JObject
。对于
JObject
,我可以使用方括号语法(类似于Dictionary
)来获取该对象中直接子属性的值。CCD_ 7给我一个CCD_ 9的CCD_。Select
方法允许我获取某个事物的IEnumerable
(在本例中为JObjects
的JArray
,表示构建),对该列表进行迭代,并对列表中的每个项应用一个函数,以便将其转换为其他事物的列表(在本案中为字符串列表)。我应用于每个构建
JObject
的函数是lambda表达式:b => b["number"] + " - " + b["artifacts"] ...
。在这个表达式中,我说"从构建b
中,将number
属性作为字符串,用分隔符-
和子表达式连接,子表达式从构建的工件列表中获得第一个文件名。在子表达式中,我得到构建的
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。只要知道你需要剔除重复的内容,这太可怕了。