C# 中 XmlNode.SelectSingleNode(string xpath) 的正确用法是啥?

Posted

技术标签:

【中文标题】C# 中 XmlNode.SelectSingleNode(string xpath) 的正确用法是啥?【英文标题】:What is the correct use of XmlNode.SelectSingleNode(string xpath) in C#?C# 中 XmlNode.SelectSingleNode(string xpath) 的正确用法是什么? 【发布时间】:2011-12-03 07:48:44 【问题描述】:

我在处理一些 XML 文件时遇到了问题(在本文末尾)。

我编写了以下代码以获取与给定Job_Name 模式相关的Job_Id 数据,该模式的所有者Job_Owner 是运行程序的用户:

List<String> jobID = new List<String>();
XmlNodeList nodes = xml.SelectNodes("//Job");
foreach (XmlNode node in nodes)

    innerNode = node.SelectSingleNode("//Job_Owner"); // SelectSingleNode here always selects the same node, but I thought it should be relative to node, not to nodes
    if (!innerNode.InnerText.Contains(Environment.UserName))
    
        continue;
    
    innerNode = node.SelectSingleNode("//Job_Name");
    if (!Regex.IsMatch(innerNode.InnerText, jobNamePattern, RegexOptions.Compiled))
    
        continue;
    
    innerNode = node.SelectSingleNode("//Job_Id");
    jobID.Add(innerNode.InnerText);

我希望node.SelectSingleNode("//Job_Name") 只在node 表示的xml 代码下寻找一个名为Job_Name 的标签。

这似乎不是正在发生的事情,因为它总是返回相同的节点,在foreach 的哪个步骤它是无关紧要的(即从nodes 更改中选择的node,但是node.SelectSingleNode("//Job_Name") 总是返回相同的内容)。

这段代码有什么问题?

提前致谢!

--

XML 文件如下所示:

<Data>
    <Job>
        <Job_Id>58282.minerva</Job_Id>
        <Job_Name>sb_net4_L20_sType1</Job_Name>
        <Job_Owner>mgirardis@minerva</Job_Owner>
        <!--more tags-->
    </Job>
    <Job>
        <!--etc etc etc-->
    </Job>
    <!--etc etc etc-->
</Data>

【问题讨论】:

【参考方案1】:

我们使用 maXbox 脚本执行了一个大型 DOM /xml /SQL 例程:

函数 GetXMLFromURLAdr_IsSame_All(apath: string): boolean; 变量 xml,节点:Olevariant; //IXMLDOM文档; nodes_row,nodes_se,nodex:olevariant; i, j:整数; sr1,sr2,basenod,basenod2,filePrefix,mysql,odbcDSN,Auftrag:字符串; 开始 xml:= CreateOleObject('Microsoft.XMLDOM') as IXMLDocument; xml.async:= 假; if xml.load(apath) then writeln('xml path load success2'); 如果 xml.parseError.errorCode 0 那么 writeln('XML 加载错误:' + xml.parseError.reason); basenod:= '/WAB/Auftragsliste/Auftrag'; 节点行:= xml.SelectNodes(basenod); writeln('auftrag 节点总数:'+itoa(nodes_row.length)) 尝试 对于 j:= 0 到 nodes_row.length-1 开始 //nodes_se:= nodes_row.item[j] 节点:= nodes_row.item[j] // writeln(node.text) sr1:= node.selectSingleNode('.//Lieferanschrift/Ort').text sr1:= sr1 + node.selectSingleNode('.//Lieferanschrift/Strasse').text sr2:= node.selectSingleNode('.//Rechnungsanschrift/Ort').text; sr2:= sr2 + node.selectSingleNode('.//Rechnungsanschrift/Strasse').text; writeln(node.selectSingleNode('.//Auftragskopf/FremdlieferscheinNr').text); Auftrag:= node.selectSingleNode('.//Auftragskopf/FremdlieferscheinNr').text writeln(node.selectSingleNode('.//Auftragskopf/FremdlieferscheinNr').text); if ANSICompareText(sr1, sr2) = 0 then begin srlist:= FindAllFiles(PDFFILEPATH,'*'+Auftrag+'_??.pdf',true); for it:= 0 to srlist.count-1 do begin writeln((srlist.strings[it])); if lCopyFile(srlist.strings[it], PDFEXPORT+extractfilename(srlist.strings[it]),true) then writeln('copyof=: '+srlist.strings[it]); end; srlist.free; srlist:= Nil; it:=0; result:= true; end else begin srlist:= FindAllFiles(PDFFILEPATH,'*'+Auftrag+'*.pdf',true); for it:= 0 to srlist.count-1 do begin if lCopyFile(srlist.strings[it], PDFEXPORT+extractfilename(srlist.strings[it]),true) then writeln('copyof<>: '+srlist.strings[it]); end; DeleteFiles(PDFEXPORT, '*RG.pdf'); DeleteFile(PDFEXPORT+'Special_'+Auftrag+'_ES.pdf'); srlist.free; result:= false end; //mk change in op fileprefix:= 'WAB'; odbcDSN:= 'advance_kmu_loc'; if filePrefix='WAB' then begin mySQL:= 'UPDATE verk_auftrag SET Status = 61 where Auftrag = '+Auftrag; writeln('order back: '+ itoa(MySQLQueryExecute2(mysql, odbcDsn, strtoint(Auftrag),true))); end; if filePrefix='WEA' then begin mySQL:= 'UPDATE verk_auftrag SET Status = 52 where Auftrag = '+Auftrag; writeln('order back: '+ itoa(MySQLQueryExecute2(mysql, odbcDsn, strtoint(Auftrag),true))); end; nodes_se:= node.selectNodes('.//Auftragspositionen/Position'); writeln('total posnod: '+itoa(nodes_se.length)) for i:= 0 to nodes_se.length - 1 do begin node:= nodes_se.item[i]; writeln('Posit=' + node.text); end;// writeln('------------------------'); end; // except writeln(exceptiontoString(exceptiontype, exceptionparam)) finally xml:= unassigned; xml:= NULL; end; end;

【讨论】:

嗨,Max,您能否提供一些有用的提示,说明此代码在做什么而 OP 的代码没有?为什么这是正确的,而他们的代码却不是?它的工作方式有什么不同?提前致谢。 该代码从基于 XML 的数据集包(客户端数据集)中解析一些日期,复制一些 pdf 文件并在此基础上更新另一个数据集。两个代码都是正确的。【参考方案2】:

这是因为您在 XPath 中使用了“//”语法。该特定语法选择文档中名为 that 的第一个节点。尝试查看 https://www.w3schools.com/xml/xpath_syntax.asp 以获取有关 XPath 语法的信息。

如果您要查找子节点,请尝试仅使用节点名称(即:'Job_Owner' 而不是 '//Job_Owner')

【讨论】:

好吧,这行得通...我知道那个语法,但听起来很合理,如果node 不包含所有文档,那么该语法不会搜索所有文档,但会只在node代表的代码内搜索 不是最好的使用方法 // 因为它会在不同级别上有多个 Job_Owner 的情况下给出不正确的值,因为它会返回任何 Job_Owner 的第一次出现。更好的方法是使用 ./Job_Owner,因为它会相对于当前节点进行搜索。 所以重点是,如果您以 / 或 // 开始查询,那么您在哪个节点上调用 SelectSingleNode() 实际上并不重要? 我实际上只是在阅读 w3schools 页面。虽然它教了如何形成 XPath 查询,但它没有解决这个问题,因为它没有提到将查询用作 SelectSingleNode() 等在特定节点对象上调用的方法的参数。 如果您将命名空间添加到根元素,这将再次中断并重现 OP 提到的行为,即使使用原始节点名称或 './nodeName' - selectsinglenode 将返回 null 每个时间【参考方案3】:

Infernex87 是正确的,Job_Owner 在这种情况下简单有效。但是,如果它不是直接孩子,您可以这样做:

.//Job_Owner

就像目录一样,. 是当前节点,所以这会查找当前节点的后代,而不是文档的根。

【讨论】:

我认为应该明确指出(不仅仅是暗示),SelectNodes() 和 SelectSingleNode() 方法可以访问整个 XML 文档,并且需要使用“./”锚定才能按预期工作.使用“./Job_Owner”在当前上下文位置根。【参考方案4】:

Infernex87 找到了原因。通过您的 XML,我想走 LINQ 路线对您来说可能是一个不错的选择。如果您想开始,Scott Gu's blog 是一个很好的资源。

【讨论】:

是的,我尝试搜索一点 LINQ to XML,但我还没有找到任何实用的东西(尽管我认为我搜索得不够多!)。感谢您的提示!

以上是关于C# 中 XmlNode.SelectSingleNode(string xpath) 的正确用法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

[C#]C#中yield return用法分析

[C#]C#中yield return用法分析

c# 在 c# 应用程序中保存配置数据的最佳方法是啥。 [复制]

在 C# 中定义 F# '**' 运算符

如何在 C# 的泛型中使用扩展

C# - 如何检查 C# 中是不是存在命名空间、类或方法?