如何使用 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
快,因为它只获取直接子代。
将IndexOf
与OrdinalIgnoreCase
一起使用,而不是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 节点的值?的主要内容,如果未能解决你的问题,请参考以下文章
如何搜索 xml 节点值,然后在 c# 中为该元素创建新属性