This is my sample XML file:
<?xml version="1.0" encoding="utf-8"?>
<receipts>
<receipt id="F66BF89E-1070-4E02-9B98-B934A66D0E16" year="2014" month="9" day="17" place="Place#1">
<purchase name="Coca-Cola" quantity="1" price="3.99" category="Drinks" />
<purchase name="Watermelon" quantity="1" price="1.98" />
<purchase name="Water" quantity="1" price="1.49" category="Drinks" />
</receipt>
<receipt id="13ECD7F9-F639-4E65-9C80-9E1A8C2E305B" year="2014" month="9" day="17" place="Place#1">
<purchase name="Sweets" quantity="1" price="3.98" category="Sweets" />
<purchase name="Water" quantity="1" price="1.99" category="Drinks" />
<purchase name="Flour" quantity="1" price="1.99" />
<purchase name="Bread" quantity="1" price="1.19" />
<purchase name="Milk" quantity="1" price="1.98" category="Drinks"/>
<purchase name="Potatoes" quantity="0.345" price="1.49" />
</receipt>
</receipts>
And this is my approach to parsing, filtering, categorizing and displaying it:
// load XML
XElement xmlReceipts = XElement.Load(@".\receipts.xml");
// create objects from XML
List<Receipt> receipts = new List<Receipt>();
foreach (var xmlReceipt in xmlReceipts.Elements("receipt"))
{
Guid id = new Guid(xmlReceipt.Attribute("id").Value);
string place = xmlReceipt.Attribute("place").Value;
int year = Int32.Parse(xmlReceipt.Attribute("year").Value);
int month = Int32.Parse(xmlReceipt.Attribute("month").Value);
int day = Int32.Parse(xmlReceipt.Attribute("day").Value);
List<ProductLine> products = new List<ProductLine>();
foreach (var xmlProduct in xmlReceipt.Elements("purchase"))
{
string name = xmlProduct.Attribute("name").Value;
string quantity = xmlProduct.Attribute("quantity").Value;
string price = xmlProduct.Attribute("price").Value;
string category = xmlProduct.Attribute("category") == null ? "(no category)" : xmlProduct.Attribute("category").Value;
products.Add(new ProductLine(name, quantity, price, category));
}
receipts.Add(new Receipt(id, place, year, month, day, products));
}
// filter to this month's receipts
var receiptsFiltered = from receipt in receipts
where receipt.Date.Year == DateTime.Now.Year &&
receipt.Date.Month == DateTime.Now.Month
select receipt.Products as List<ProductLine>;
// flatten lists of receipts and products
var productsFiltered = from products in receiptsFiltered
from product in products
select new { Name = product.Name, Price = product.Price, Quantity = product.Quantity, Category = product.Category };
// group products by category
var productsByCategory = from product in productsFiltered
group product by product.Category;
// calculate totals per category
Dictionary<string, double> categoriesTotals = new Dictionary<string, double>();
foreach (var category in productsByCategory)
{
double totalSum = 0;
foreach (var product in category)
{
totalSum += product.Quantity * (double)product.Price;
}
categoriesTotals.Add(category.Key, totalSum);
}
// show data on chart
chart1.Series[0].Points.DataBindXY(categoriesTotals.Keys, categoriesTotals.Values);
Is there a way to simplify it? I tried to use XML to LINQ or linking XML directly to Chart but I couldn't manage grouping before flattening the lists.
Sources: