[英]LINQ / Xpath query for ungrouped and repeated XML elements
我是.NET的新手,在LINQ to XML中實現查詢時遇到了一些麻煩。
我有一個格式奇怪的XML文件:
<calendar>
<event>
<amount>1200</amount>
<age>40</age>
<country>FR</country>
<amount>255</amount>
<age>16</age>
<country>UK</country>
<amount>10524</amount>
<age>18</age>
<country>FR</country>
<amount>45</amount>
<age>12</age>
<country>CH</country>
<event>
<event>
<amount>1540</amount>
<age>25</age>
<country>UK</country>
<amount>255</amount>
<age>31</age>
<country>CH</country>
<amount>4310</amount>
<age>33</age>
<country>FR</country>
<amount>45</amount>
<age>17</age>
<country>FR</country>
<event>
</calendar>
從這個文件中,我想計算每個<amount>
元素值的總和,其中<age>
大於'20',而<country>
是'FR'或'CH'。
此操作獨立於標記<event>
(檢查上述條件的所有<amount>
元素應加在一起,無論它們處於相同還是不同的<event>
元素下)。
我的問題是我沒有將<amount>
, <age>
和<country>
組合在一起的元素標記...(我無法更改XML格式,我從無法訪問的Web服務中使用它)。
如果我有一個假設的<transfer>
標簽將這些三元組組合在一起,我認為代碼將很簡單:
XElement root = XElement.Load("calendar.xml");
IEnumerable<XElement> sum =
from trf in root.Elements("events").Elements("transfers")
where (decimal) trf.Element("age") > 20 &&
((string) trf.Element("Country") == "FR" ||
(string) trf.Element("Country") == "cH")
select trf.Element("Amount").Sum();
我應該以編程方式對這些元素進行分組嗎? 提前致謝!
嘗試這個:
var xe = XElement.Load(@"calendar.xml");
var langs = new List<string> { "FR", "CH" };
var sum = xe.Descendants("amount")
.Where(e =>
Convert.ToInt32(e.ElementsAfterSelf("age").First().Value) > 20 &&
langs.Any(l => l == e.ElementsAfterSelf("country").First().Value))
.Select(e => Convert.ToDouble(e.Value)).Sum();
我已經測試了代碼。 您還必須確保數量元素必須是組中的第一個元素。
如果您是我,那么我將對Xml進行預處理(也許可以使用XmlReader逐個節點讀取它),並以更分層的結構讀取它。 這將使搜索元素以及對元素進行排序或過濾而不會丟失它們之間的關系(現在僅基於它們的順序)變得更加容易。
編輯(請參閱評論中的討論)
據我所知,xml規范沒有說元素的順序很重要,因此您使用的解析器(或整個Xml的任何預處理或對其Xml的提取)元素)可以在相同級別上更改金額,年齡和國家/地區元素的順序。
雖然我認為大多數操作都傾向於保留文檔順序,但是由於隨機重新排序而導致的細微和難以發現的錯誤的可能性不會讓我睡得太香。
好吧...我不確定您將如何在LINQ中完成此操作,但這是一個XPath查詢,它對您提供的數據適用於我:
編輯:
返回節點:
//*[text()='FR' or text()='CH']/preceding::age[number(text())>20][1]/preceding::amount[1]
返回總和:
sum(//*[text()='FR' or text()='CH']/preceding::age[number(text())>20][1]/preceding::amount[1]/text())
用途 :
sum(/*/*/amount
[following-sibling::age[1] > 20
and
contains('FRCH',
following-sibling::country[1])
])
基於XSLT的驗證 :
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:value-of select=
"sum(/*/*/amount
[following-sibling::age[1] > 20
and
contains('FRCH',
following-sibling::country[1])
])"/>
</xsl:template>
</xsl:stylesheet>
當此轉換應用於提供的XML文檔時 :
<calendar>
<event>
<amount>1200</amount>
<age>40</age>
<country>FR</country>
<amount>255</amount>
<age>16</age>
<country>UK</country>
<amount>10524</amount>
<age>18</age>
<country>FR</country>
<amount>45</amount>
<age>12</age>
<country>CH</country>
</event>
<event>
<amount>1540</amount>
<age>25</age>
<country>UK</country>
<amount>255</amount>
<age>31</age>
<country>CH</country>
<amount>4310</amount>
<age>33</age>
<country>FR</country>
<amount>45</amount>
<age>17</age>
<country>FR</country>
</event>
</calendar>
計算XPath表達式並輸出所需的正確結果 :
5765
請注意 : 當前選擇的答案包含錯誤的XPath表達式,並且它們產生的總和是錯誤的 。 請參見下面的XSLT轉換中所示的內容(第一個數字是正確的結果,第二個數字是使用來自公認答案的XPath表達式生成的:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:value-of select=
"sum(/*/*/amount
[following-sibling::age[1] > 20
and
contains('FRCH',
following-sibling::country[1])
])"/>
============
<xsl:value-of select="sum(//*[text()='FR' or text()='CH']/preceding::age[number(text())>20][1]/preceding::amount[1]/text())"/>
</xsl:template>
</xsl:stylesheet>
結果 :
5765
============
12475
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.