【问题标题】:How can I search values of consecutive XML nodes with C#?如何使用 C# 搜索连续 XML 节点的值?
【发布时间】: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 为单位运行。

标签: c# xml linq xpath linq-to-xml


【解决方案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);
                  }
              }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多