如何使用 C# 搜索连续 XML 节点的值?

Posted

技术标签:

【中文标题】如何使用 C# 搜索连续 XML 节点的值?【英文标题】:How can I search values of consecutive XML nodes with C#? 【发布时间】:2021-07-02 10:15:14 【问题描述】:

我想从 XML 中选择具有连续子节点的节点,这些节点的值与我的搜索词中的相应单词匹配。

这是一个示例 XML:

<book name="Nature">
<page number="4">
    <line ln="10">
        <word wn="1">a</word>
        <word wn="2">white</word>
        <word wn="3">bobcat</word>
        <word wn="3">said</word>
    </line>
    <line ln="11">
        <word wn="1">Hi</word>
        <word wn="2">there,</word>
        <word wn="3">Bob.</word>
    </line>
</page>

我的搜索词是 Hi Bob。我想从上面的 XML 中找到所有节点,其中包含两个 连续 单词,其值为 %Hi%%Bob%。请注意,我想对搜索词中的每个单词执行部分且不区分大小写的匹配。

它应该为上述 XML 返回以下输出:

ln="10" wn="2" wn="3"

请注意,选择行 (ln=10) 是因为它包含与搜索词匹配的两个连续单词(按正确顺序)。白=%Hi% 山猫=%Bob%

但是,下一行 (ln=11) 没有被选中,因为匹配的节点不连续。

请注意,搜索词中的所有单词都应匹配,才能被视为匹配。

谢谢!

[编辑] 我尝试了以下解决方案,它产生了预期的结果。有没有更好或更有效的方法来实现这一目标?该程序每天必须搜索 100,000 个 XML 文件,每个文件大小为 300 KB 到 50 MB。

XDocument xDoc = XDocument.Load(@"C:\dummy.xml");
        var xLines = xDoc
                    .Descendants("page")
                    .Descendants("line");
        
        foreach (var xLine in xLines)
        
            var xFirstWords = xLine
                                .Descendants("word")
                                .Where(item => item.Value.ToUpper().Contains("HI"));
            
            foreach (var xFirstWord in xFirstWords)
            
                var xNextWord = xFirstWord.NodesAfterSelf().OfType<XElement>().First();
                if(xNextWord.Value.ToUpper().Contains("BOB"))
                
                    MessageBox.Show(xLine.FirstAttribute.Value + " " + xFirstWord.FirstAttribute.Value + " " + xNextWord.FirstAttribute.Value);
                
            
        

【问题讨论】:

不是我的反对票,但是...这不是免费的编码服务。如果您需要帮助,您需要表现出真诚的努力来尝试自己解决问题。你试过什么?结果与您想要的结果有何不同? 非常感谢,尼古拉斯。这完全有道理。我编辑了问题并添加了我的代码。它给出了预期的结果。但是,我不确定这是否是最有效的方法。我必须搜索数千个 XML,每个都以 MB 为单位运行。 【参考方案1】:

从性能方面想到的事情:

.Nodes 会比 .Descendants 快,因为它只获取直接子代。 将IndexOfOrdinalIgnoreCase 一起使用,而不是ToUpper.Contains。 在foreach而不是NodesAfterSelf中,你可以只保留前一个节点。
var xLines = xDoc.Descendants("line");
        
foreach (var xLine in xLines)

    XNode prevWord = null;
    foreach (var word in xLine.Nodes("word"))
    
        if(prevWord == null && word.Value.IndexOf("HI", StringComparison.OrdinalIgnoreCase))
        
            prevWord == word;
        
        else if(prevWord != null && word.Value.IndexOf("BOB"), StringComparison.OrdinalIgnoreCase))
        
            MessageBox.Show(xLine.FirstAttribute.Value + " " + prevWord.FirstAttribute.Value + " " + word.FirstAttribute.Value);
        
    

【讨论】:

【参考方案2】:

我不知道这段代码的性能会更好还是更差,但我很确定它会有所不同,所以值得一试。重构该行的文本,然后使用正则表达式进行匹配。

Regex re = new Regex(@"^.*Hi\s+\S+\s+Bob$*", RegexOptions.IgnoreCase);
XDocument xDoc = XDocument.Load(@"C:\Users\user\Documents\temp.xml");
foreach (XElement xLine in xDoc.Root.Descendants("line")) 
    string text = string.Join(" ", xLine.Elements("word").Select(x => x.Value));
    if (re.IsMatch(text)) 
        Console.WriteLine(text);
    

【讨论】:

【参考方案3】:

我可以即兴创作我的代码。如果您有更好的解决方案,请告诉我。

  XDocument xDoc = XDocument.Load(@"C:\dummy.xml");
  var xLines = xDoc
              .Descendants("page")
              .Descendants("line");
  foreach (var xLine in xLines)
        
            var xFirstWords = xLine
                                .Descendants("word")
                                .Where(item => item.Value.ToUpper().Contains("HI"))
                                .Where(item => item.ElementsAfterSelf("word").First().Value.ToUpper().Contains("BOB"));
                                

            foreach (var xFirstWord in xFirstWords)
            
                var xNextWord = xFirstWord.ElementsAfterSelf("word").First();
                MessageBox.Show(xLine.FirstAttribute.Value + " " + xFirstWord.FirstAttribute.Value + " " + xNextWord.FirstAttribute.Value);
            
        

【讨论】:

以上是关于如何使用 C# 搜索连续 XML 节点的值?的主要内容,如果未能解决你的问题,请参考以下文章

C#如何读取XML中指定的节点值?

如何搜索 xml 节点值,然后在 c# 中为该元素创建新属性

C#如何读取xml文件里面节点里面的属性信息?

C# XML 如何直接根据属性值 读取 对应的值 怎么写???

C# 操作xml 在指定节点插入新节点,该如何操作?

C#中treeview控件中,如何使第一个节点默认选中高亮?