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

How can I search values of consecutive XML nodes with C#?

我想 select 来自 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>

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

上面XML应该return输出如下:

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

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

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

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

谢谢!

[编辑] 我尝试了以下解决方案,它产生了预期的结果。有没有更好或更有效的方法来实现这一目标?该程序每天必须搜索 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);
                }
            }
        }

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

  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);
            }
        }

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

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);
    }
}

在性能方面想到的事情:

  • .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);
        }
    }
}