我有一個 XML 檔案,其中包含大約 7000 個總節點(每行一個節點,沒有級聯節點),每個節點有大約 15 到 20 個保存十進制值的屬性。xml 檔案大小約為 3 到 4 Mb。在每個節點中,符號屬性都有一個唯一的值。
目標是通過匹配“符號”屬性來搜索節點。
我有以下列出的方法,它將符號串列作為輸入(symbolList)。為了執行搜索,XPathDocument 從硬碟加載 XML 檔案,對回圈中的每個符號執行搜索,并以字典的形式回傳結果。這些符號(輸入)可以是 10 或 100 等(它不是固定的)
為了執行搜索,我為每個符號運行了一個 for-each 回圈。
問題:
(1) 在一次搜索中搜索所有符號并消除一次搜索一個符號的回圈的另一種有效方法是什么。
在下面的代碼中,我對效率不滿意。XPathNavigator 在回圈中一次執行對一個符號的搜索,它檢索匹配的節點、讀取屬性值并在集合中添加值。我想洗掉一次搜索一個符號的回圈。
我想通過添加所有帶有“或”條件的符號來構建一個 XPath 查詢,但是當我有 100 個左右的符號要搜索時,它可能是一個很大的 XPath 查詢。有沒有更好的解決方案來減少掃描次數?
(2) 如何利用 XPath 查詢“編譯”進行動態搜索?
我可以編譯 XPath 查詢來構建 XPathExpression,但這只有在我的 XPath 對多次掃描保持相同時才有幫助,而且我沒有找到編譯查詢的方法,我可以將搜索 @parameter 值提供給編譯的查詢. 有沒有辦法或任何示例將 Xslt 模板(作為字串)與引數一起使用?
(3) 有沒有其他建議可以減少 CPU 周期并使此代碼比當前運行得更快?我并不是說這段代碼很慢,但我想讓它盡可能快。
Xml 檔案示例:
<?xml version="1.0" encoding="utf-8"?>
<items>
<item symbol="ABC" val1="46.21717" val2="152.39" val3="158.121" />
<item symbol="CJKM" val1="51.21659" val2="49.8" val3="57.57" />
<item symbol="FWML" val1="67.99509" val2="9.75" val3="9.84" />
<item symbol="JSHR" val1="48.67459" val2="2.27" val3="2.9" />
<item symbol="DIBG" val1="53.60444" val2="26.04" val3="28" />
<item symbol="GHLH" val1="42.31754" val2="0.1016" val3="0.1192" />
<item symbol="ICWE" val1="58.39788" val2="3.855" val3="3.99" />
<item symbol="LPVN" val1="47.03581" val2="19.22" val3="20.15" />
<item symbol="MCAT" val1="57.83422" val2="23.0969" val3="26.59" />
<item symbol="ZYXI" val1="54.94584" val2="11.6784" val3="12.9" />
</items>
C# 代碼:
using System.Collections.Generic;
using System.IO;
using System.Xml.XPath;
namespace Library
{
public class Info
{
public float Val1 { get; set; }
public float Val2 { get; set; }
public float Val2 { get; set; }
}
public class Technical
{
public Dictionary<string, Info> SearchForSymbols(HashSet<string> symbolList)
{
Dictionary<string, Info> dictSearchResult = new Dictionary<string, Info>();
if (symbolList.Count == 0)
{
return dictSearchResult;
}
FileInfo fileInfo = new FileInfo(Path.Combine(CommonObjects.Constant.SettingsFolderPath, CommonObjects.Constant.TechnicalFileName));
XPathDocument document = new XPathDocument(fileInfo.FullName);
XPathNavigator navigator = document.CreateNavigator();
string symbol;
float val1;
float val2;
float val3;
XPathNavigator node;
Info info;
foreach (string item in symbolList)
{
XPathExpression expression = navigator.Compile($"//items/item[@symbol='{item}']");
node = navigator.SelectSingleNode(expression);
if (node == null)
{
continue;
}
symbol = node.GetAttribute("symbol", "");
if (string.IsNullOrEmpty(symbol))
{
continue;
}
info = new Info();
// Get Value 1
if (float.TryParse(node.GetAttribute("val1", ""), out val1))
{
info.Val1 = val1;
}
// Get Value 2
if (float.TryParse(node.GetAttribute("val2", ""), out val2))
{
info.Val2 = val2;
}
// Get Value 3
if (float.TryParse(node.GetAttribute("val3", ""), out val3))
{
info.Val3 = val3;
}
if (!dictSearchResult.ContainsKey(symbol))
{
dictSearchResult.Add(symbol, info);
}
}
return dictSearchResult;
}
}
}
uj5u.com熱心網友回復:
這基本上是一個連接查詢,你的演算法是一個嵌套回圈連接,這絕對不理想。您在回圈中重新編譯 XPath 運算式這一事實加劇了問題。
要搜索 100 個符號,最好將這些符號放入某種查找結構(例如 HashSet)中,并對輸入進行單次掃描以測驗每個專案以查看其鍵是否存在于 HashSet 中。
理想情況下,您不希望將邏輯拆分為兩種不同的語言(C# 和 XPath)。我不知道使用 Microsoft 技術的呼叫開銷有多高,但肯定是可觀的。
使用 XPath 3.1(在 SaxonCS 中可用),您可以在單個 XPath 運算式中完成所有這些操作:
let $symbols := map{"AAA":1, "BBB":1, "CCC":1}
return //items/item[map:contains($symbols, @symbol)]
! map{ "symbol": string(@symbol),
"val1": number(@val1),
"val2": number(@val2),
"val3": number(@val2) }
然后有一次從 C# 到 XPath 的呼叫,在 API 層進行了一些操作,以提供符號的輸入串列并提取輸出。
但是,如果您想堅持使用當前的技術,通過對資料進行單次掃描并根據所需符號的字典測驗每個專案,仍然可以進行相當大的改進。
uj5u.com熱心網友回復:
對于您的問題,這可能是一個完全不同的實作,但是您是否嘗試將 XML 檔案加載到DataSet. 像這樣的事情:
Dictionary<string, Info> dictSearchResult = new Dictionary<string, Info>();
DataSet ds = new DataSet();
ds.ReadXml(filePath);
DataTable dt = ds.Tables[0];
您的資料表將如下所示
然后使用 LINQ 搜索您的符號,如下所示:
IEnumerable<DataRow> searchedSymbolsRows = from r in dt.AsEnumerable()
join s in symbolList
on r.Field<string>("symbol") equals s
select r;
然后你可以對你的DataRow收藏做任何你想做的事情,例如
foreach (DataRow item in searchedSymbolsRows)
{
string symbol = item[0].ToString();
float val1;
float val2;
float val3;
var symbolInfo = new Info
{
Val1 = float.TryParse(item[1].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out val1) ? val1 : 0,
Val2 = float.TryParse(item[2].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out val2) ? val2 : 0,
Val3 = float.TryParse(item[3].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out val3) ? val3 : 0,
};
dictSearchResult.Add(symbol,symbolInfo);
Console.WriteLine($"Symbol {symbol} added to dictionary");
}
注意:我不知道此方法是否會比您的實際實作具有更好的性能,但它可以是另一種選擇。
uj5u.com熱心網友回復:
我的偏好是將 XML Linq 與字典一起使用:
using System;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using System.Text;
using System.Collections;
using System.Collections.Generic;
namespace ConsoleApp2
{
class Program
{
const string FILENAME = @"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
Dictionary<string, Info> dict = doc.Descendants("item")
.Select(x => new Info() { symbol = (string)x.Attribute("symbol"), Val1 = (float)x.Attribute("val1"), Val2 = (float)x.Attribute("val2"), Val3 = (float)x.Attribute("val3") })
.GroupBy(x => x.symbol, y => y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
}
}
public class Info
{
public string symbol { get; set; }
public float Val1 { get; set; }
public float Val2 { get; set; }
public float Val3 { get; set; }
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/337088.html
標籤:C# xml 路径 xpathnavigator
