如何根据特定条件对XML文件中的值求和

时间:2014-11-02 06:50:37

标签: c# xml linq

我需要根据某些标准总结XML文件中的值 我的XMl如下

<?xml version="1.0" encoding="utf-8"?>
<transactions>
    <transcation ID="1">
        <BankName>ABC</BankName>
        <TemplatModel>CT1</TemplatModel>
        <Date>12/12/2014</Date>
        <Payeename>xyz</Payeename>
        <Amount>200</Amount>
    </transcation>
    <transcation ID="2">
        <BankName>EFG</BankName>
        <TemplatModel>CT2</TemplatModel>
        <Date>12/12/2014</Date>
        <Payeename>xyz</Payeename>
        <Amount>100</Amount>
    </transcation>
    <transcation ID="3">
        <BankName>ABC</BankName>
        <TemplatModel>CT1</TemplatModel>
        <Date>10/12/2014</Date>
        <Payeename>pqr</Payeename>
        <Amount>400</Amount>
    </transcation>
    <transcation ID="4">
        <BankName>EFG</BankName>
        <TemplatModel>CT2</TemplatModel>
        <Date>11/12/2014</Date>
        <Payeename>asd</Payeename>
        <Amount>300</Amount>
    </transcation>
</transactions>

我需要找到 BankName =“ABC”的金额总和,在另一个查询中,我需要查找日期小于“12/12/2014”的金额总和

还有一个人怀疑哪个xml结构更好

<transactions>
    <transcation ID="1" BankName="ABC" TemplatModel="CT1" Date="12/12/2014" Payeename="asd" Amount="200"/>
    <transcation ID="2" BankName="EFG" TemplatModel="CT2" Date="12/12/2014" Payeename="xyz" Amount="100"/>
    <transcation ID="3" BankName="ABC" TemplatModel="CT1" Date="10/12/2014" Payeename="pqr" Amount="400"/>
    <transcation ID="4" BankName="EFG" TemplatModel="CT2" Date="11/12/2014" Payeename="asd" Amount="300"/>
</transactions>

编辑问题

将查询结果返回到List

时出错
 public static List<string> banksearch(string bankname, string sdate = null, string edate = null, string condition = null)
    { 
    }

return transactions
                        .Where(t => t.BankName == bankname && t.Date == startdate)
                         .Select(t => new
                                 {
                                     TransactionID = t.TransactionID,
                                     BankName = t.BankName,
                                     TemplateModel = t.TemplateModel,
                                     Date = t.Date,
                                     PayeeName = t.PayeeName,
                                     Amount = t.Amount
                                 }).ToList();

我的错误是

Error   2   Cannot implicitly convert type 'System.Collections.Generic.List<AnonymousType#1>' to 'System.Collections.Generic.List<string>'

3 个答案:

答案 0 :(得分:3)

您可以使用LINQ to XML:

DateTime exclusiveDateTime = new DateTime(2014, 12, 12);

// instantiate and load your XDocument as yourXDocument here

XElement transcationsElement = yourXDocument.Root;

IEnumerable<int> amounts = from transcation in transcationsElement.Descendants("transcation")
                           where transcation.Attribute("BankName").Value == "ABC" &&
                               Convert.ToDateTime(transcation.Attribute("Date").Value) < exclusiveDateTime
                           select Convert.ToDecimal(transcation.Attribute("amount"));

decimal amountsSum = amounts.Sum();

我正在使用您发布的第二个XML表单,因为我发现处理元素的相关属性比元素的子元素更容易,但这主要是个人偏好。此解决方案假定您的Date和Amount属性中的每一个都分别正确地形成为DateTime字符串和整数。如果不是这种情况,你会遇到InvalidCastExceptions。

注意 - 如果您正在考虑扩展XML,并且需要对transcation元素执行更多查询,则应该创建一个类来封装每个元素中包含的数据,并且使用LINQ to Objects来解析数据并实例化每个对象。

答案 1 :(得分:2)

我将采用略微不同的方法。在您的问题中,您提到了两个查询,我可以看到您需要其他查询。在这种情况下,我建议将XML解析为一个对象并拥有那些您可以查询的对象的集合。

首先,保存事务数据的对象(类):

public class Transaction
{

    public int TransactionID { get; set; }
    public string BankName { get; set; }
    public string TemplatModel {get; set; }
    public DateTime Date { get; set; }
    public string PayeeName { get; set; }
    public decimal Amount { get; set; }
}

请注意,我选择小数来表示金额 - 这是货币单位的正常选择。

接下来,您可以使用LINQ to XML将XML解析为Transaction个对象的集合,就像这样(基于您的第一个XML),假设您有一个名为{{1}的XDocument表示XML文件:

xDoc

请注意List<Transaction> transactions = xDoc.Root.Elements("transcation") .Select(x => new Transaction() { TransactionID = Convert.ToInt32((string)x.Attribute("ID")), BankName = (string)x.Element("BankName"), TemplatModel = (string)x.Element("TemplatModel"), Date = Convert.ToDateTime((string)x.Element("Date")), PayeeName = (string)x.Element("Payeename"), Amount = Convert.ToDecimal((string)x.Element("Amount")) }).ToList(); 的使用 - 这将优雅地处理缺少的元素,而不会抛出空引用异常。

最后,使用您的(string)集合,您可以运行所需的任何类型的查询,包括您在问题中提到的两种查询:

transactions

var banksByName = transactions.Where(t => t.BankName == "ABC")

这种方法的优点是您可以一次查询XML以开发对象集合,然后您可以根据需要经常查询该集合。

已修改为新错误添加修补程序

您收到DateTime selectedDate = new DateTime(2014, 12, 22); var transByDate = transactions.Where(t => t.Date < selectedDate); 错误的原因是您的方法预计会返回Cannot implicitly convert type 'System.Collections.Generic.List<AnonymousType#1>' to 'System.Collections.Generic.List<string>',并且您实际上正在返回List<string> 。如果在没有具体类的LINQ Select语句中使用List<AnonymousType>关键字,则需要创建一个匿名类型,该类型不能在方法中返回。

要解决此问题,请将方法的返回类型更改为new,并在List<Transaction>关键字后添加Transaction

new

答案 2 :(得分:1)

我会根据第一个查询帮助您查询一个查询(金额总和)和另一个查询

int sum = 0;
transactionXml.Descendants("transaction").ToList().ForEach(i => {
    if (String.Equals(i.Element("BankName").Value, "abc", StringComparison.OrdinalIgnoreCase))
        sum += Convert.ToInt32(i.Element("Amount").Value);
});

关于XML结构,银行名称和收款人姓名可以包含可以破坏您的xml的特殊字符。因此建议的XML结构如下

<transactions>
    <transcation ID="1" TemplatModel="CT1" Date="12/12/2014" Amount="200">
        <BankName>ABC</BankName>
        <Payeename>asd</Payeename>
    </transcation>
    ***
    ***
    ***
</transactions>