How to read xml file using linq - c#

I have this db.xml file
<items>
<item>
<title>Title1</title>
<year>2013</title>
<categories>
<category>Category1</category>
<category>Category2</category>
<category>Category3</category>
</categories>
<count>10</count>
</item>
(and so on)
</items>
I read like that:
var items = from item in xdoc.Descendants("item")
select new
{
Title = item.Element("title").Value,
Year = item.Element("year").Value,
Categories = item.Element("categories").Value, // I know this is wrong
Count = item.Element("count").Value
};
The problem is how I can read the categories and add them to list?
foreach (var item in items)
{
book.Title = item.Title;
book.Year = item.Year;
foreach (var Category in Categories)
{
book.Categories.Add(Category);
}
book.Count = item.Count;
books.Add(book);
}

It's better to use casting (to string, to int, etc then reading element's value directly. Here is query which returns integer values for Year and Count properties. Categories are IEnumerable<string>:
var items = from item in xdoc.Descendants("item")
select new {
Title = (string)item.Element("title"),
Year = (int)item.Element("year"),
Count = (int)item.Element("count"),
Categories = from c in item.Element("categories").Elements()
select (string)c
};
If you want Categories as List<string> then parse categories this way:
Categories = item.Element("categories")
.Elements()
.Select(c => (string)c)
.ToList()

You can take the list of its elements
EDITED
var items = from item in xdoc.Descendants("item")
select new
{
Title = item.Element("title").Value,
Year = item.Element("year").Value,
Categories = item.Descendants("categories").Descendants().Select(x=>x.Value).ToList(),
Count = item.Element("count").Value
};

Related

Saving values into List in a class by using LINQ

I have a class Item with a string List ImagesUrl property:
public class Item
{
string Name { get; set; }
List<string> ImagesUrl { get; set; }
..
}
And I want to parse a XML file and save each node item into var items. In node item there are nodes img1, .. , img5 and right these I want to save into the Img property by using LINQ command like this:
var items = from item in xmlDocument.Descendants("item")
select new Item
{
Name = item.Element("name").Value,
ImagesUrl = item.Element("img1").Value, //....?
..
};
How you can see, I don't know how I could save the img1..5 values in LINQ. Could someone help me?
var items = from item in xmlDocument.Descendants("item")
select new Item
{
Name = item.Element("name").Value,
ImagesUrl = Enumerable.Range(1,5).Select(x => item.Element("img"+x).Value).ToList();
};
Code is self-explanatory here.
Universal solution independent on count of imgX elements
var items = from item in Xml.Descendants("item")
select new Item
{
Name = item.Element("name").Value,
ImagesUrl = item.Elements()
.Where(e => e.Name.LocalName.StartsWith("img"))
.Select(e => e.Value)
.ToList()
};

Parse Hierarchial Linq to XML

I am trying to parse this data:
<Product>
<ProductName>Climate Guard</ProductName>
<Tag>ClimateGuard</Tag>
<SupportPage>~/Support/ClimateGuard.aspx</SupportPage>
<ProductPage>~/Products/ClimateGuard.aspx</ProductPage>
<ProductCategories>
<ProductCategory>Climate Guard</ProductCategory>
<PartNumbers>
<PartNumber Primary="true">CLIMATE GUARD</PartNumber>
<PartNumber>CLIMATEGUARD LT</PartNumber>
<PartNumber>CLIMATE GUARD STARTER KIT</PartNumber>
<PartNumber>SENSOR MODULE</PartNumber>
<PartNumber>SWCH INP MODULE</PartNumber>
<PartNumber>TEMP SENSOR</PartNumber>
<PartNumber>HUMIDITY SENSOR</PartNumber>
<PartNumber>DOOR CONTACT</PartNumber>
<PartNumber>MOTION SENSOR</PartNumber>
<PartNumber>FLOOD DETECTOR</PartNumber>
<PartNumber>SMOKE DETECTOR</PartNumber>
<PartNumber>TILT SENSOR</PartNumber>
<PartNumber>SENSOR CABLE</PartNumber>
<PartNumber>PWR INP CABLE</PartNumber>
<PartNumber>100FT 2-WIRE</PartNumber>
<PartNumber>RJ25 COUPLER</PartNumber>
</PartNumbers>
</ProductCategories>
<Downloads>
<Download>
<Version>1.0.27</Version>
<Url>~/Files/Downloads/ClimateGuard_Firmware_1_0_27.bin</Url>
<Comment>Firmware</Comment>
</Download>
<Download>
<Version>1.0.6</Version>
<Url>~/Files/Downloads/ClimateGuard_BuiltInModule_1_0_6.bin</Url>
<Comment>Built-in Module</Comment>
</Download>
<Download>
<Version>1.0.2</Version>
<Url>~/Files/Downloads/ClimateGuard_SensorModule_1_0_2.bin</Url>
<Comment>Sensor Module</Comment>
</Download>
<Download>
<Version>1.0.0</Version>
<Url>~/Files/Downloads/ClimateGuard_SwitchInputModule_1_0_0.bin</Url>
<Comment>Switch Input Module</Comment>
</Download>
</Downloads>
</Product>
I am trying to get a List of part numbers, however, only the first appears:
Product Category Climate Guard
Part Number Climate Guard
What is wrong with my part numbers code:
public List<Products> GetProducts()
{
XElement myElement = XElement.Load(HttpContext.Current.Server.MapPath("~/App_Data/products.xml"));
var query = from a in myElement.Elements("Product")
select new Products
{
ProductName = a.Element("ProductName").Value,
Tag = a.Element("Tag").Value,
SupportPage = a.Element("SupportPage").Value,
ProductPage = a.Element("ProductPage").Value,
ProductCategories = from b in a.Elements("ProductCategories")
select new ProductCategories
{
ProductCategory = b.Element("ProductCategory").Value,
//PartNumbers = GetPartNumbers(myElement.Elements("Product").Elements("ProductCategories").Elements("PartNumbers").Elements("PartNumber"))
PartNumbers = from c in b.Elements("PartNumbers")
select new PartNumbers
{
PartNumber = c.Element("PartNumber").Value
}
},
Downloads = from bb in a.Elements("Downloads").Elements("Download")
select new Downloads
{
Comment = bb.Element("Comment").Value,
Url = bb.Element("Url").Value,
Version = bb.Element("Version").Value
},
};
return query.ToList();
}
All of the types (ProductName, Tag, etc.) are strings. PartNumbers is an IEnumerable.
Currently instead of getting collection of PartNumber element values, you are getting only element for their parent PartNumbers with value of first PartNumber child inside. If you want to have PartNumbers class instead of simple list of string values, then it should look like:
public class PartNumbers
{
// list instead of single value
public List<string> Numbers { get; set; }
}
And it should be parsed this way:
PartNumbers = new PartNumbers {
Numbers = b.Element("PartNumbers").Elements()
.Select(c => (string)c).ToList()
}
BTW why are you choosing so strange range variable names (b for ProductCategories elements, a for products, etc)? Also you can use simple List<string> to store part numbers (without creating class for that):
PartNumbers = b.Element("PartNumbers").Elements().Select(c => (string)c).ToList()
You may have forgotten the ToList() for ProductCategories, PartNumbers and Downloads.
public List<Products> GetProducts()
{
XElement myElement = XElement.Load(HttpContext.Current.Server.MapPath("~/App_Data/products.xml"));
var query = from a in myElement.Elements("Product")
select new Products
{
ProductName = a.Element("ProductName").Value,
Tag = a.Element("Tag").Value,
SupportPage = a.Element("SupportPage").Value,
ProductPage = a.Element("ProductPage").Value,
ProductCategories = (from b in a.Elements("ProductCategories")
select new ProductCategories
{
ProductCategory = b.Element("ProductCategory").Value,
//PartNumbers = GetPartNumbers(myElement.Elements("Product").Elements("ProductCategories").Elements("PartNumbers").Elements("PartNumber"))
PartNumbers = (from c in b.Elements("PartNumbers")
select new PartNumbers
{
PartNumber = c.Element("PartNumber").Value
}).ToList()
}).ToList(),
Downloads = (from bb in a.Elements("Downloads").Elements("Download")
select new Downloads
{
Comment = bb.Element("Comment").Value,
Url = bb.Element("Url").Value,
Version = bb.Element("Version").Value
}).ToList(),
};
return query.ToList();
}

WPF Treeview using linq to entities multiple relations

I don't know what I'm missing on this:
I have this relationship in my EDM- (can't load a picture yet)
Category_1__many RecipeCategory_many__1_Recipe
Basically every recipe has one or more categories and every category can have 0 or more recipes
Here's my code:
private void LoadData()
{
List<Category> Categories = new List<Category>();
List<Recipe> Recipes = new List<Recipe>();
var ctx = new MaWEntities();
var query = from x in ctx.Categories select x;
Categories = query.ToList<Category>();
foreach (Category c in Categories)
{
TreeViewItem TVP = new TreeViewItem() { Header = c.CategoryName.ToString() };
var qq = from xx in ctx.RecipeCategories.Where(o => o.CategoryId == c.CategoryId) select xx;
foreach (var rc in qq)
{
TreeViewItem TVC = new TreeViewItem() { Header = rc.Recipe.RecipeName.ToString() };
TVP.Items.Add(TVC);
//TVP.IsExpanded = true;
}
}
}
What I am trying to accomplish is a treeview whose parent nodes are the categoryname and child nodes are the recipe name. I know how drill into a relational table from a base table (Recipe to RecipeCategory) using Linq. There MUST also be a way to traverse again to the Category table too. I should also mention that even if the Category does not have any Recipes I still want to see the Category Name as a parent in the treeview.
I found that my problem in this scenario was not that my code wasn't working; rather that I neglected to ever add the treeview items to my treeview. Also, this addition needs to be done after the foreach loop. List Categories = new List();
List Recipes = new List();
var ctx = new MaWEntities();
var query = from x in ctx.Categories select x;
Categories = query.ToList<Category>();
foreach (Category c in Categories)
{
TreeViewItem TVP = new TreeViewItem() { Header = c.CategoryName.ToString() };
var query2 = from xx in ctx.RecipeCategories.Where(o => o.CategoryId == c.CategoryId) select xx;
foreach (var rc in query2)
{
TreeViewItem TVC = new TreeViewItem() { Header = rc.Recipe.RecipeName.ToString() };
TVP.Items.Add(TVC);
}
tvwRecipe.Items.Add(TVP);
}

One to Many Linq to XML query

I have an XML string that looks like this:
<Attributes>
<ProductAttribute id="1">
<ProductAttributeValue>
<Value>a</Value>
</ProductAttributeValue>
</ProductAttribute>
<ProductAttribute id="2">
<ProductAttributeValue>
<Value>a</Value>
</ProductAttributeValue>
<ProductAttributeValue>
<Value>b</Value>
</ProductAttributeValue>
</ProductAttribute>
</Attributes>
I would like to return an IEnumerable like this:
Id Value
1 a
2 a b
I have tried this and only got the "b" value for Id "2":
XElement e = XElement.Parse(xmlString);
var q = from pa in e.Elements("ProductAttribute")
from pav in pa.Elements("ProductAttributeValue").Elements("Value")
select new
{
Id = (int)pa.Attribute("id"),
Value = (string)pav
};
I tried this:
XElement e = XElement.Parse(xmlString);
var q = from pa in e.Elements("ProductAttribute")
select new
{
Id = (int)pa.Attribute("id"),
Value = pa.Elements("ProductAttributeValue").Elements("Value")
};
But could not cast Value as a string. Using LINQPad the output was like this:
Id Value
1 a
2 <Value>a</Value>
<Value>b</Value>
I am trying to just return the values. Is this even possible?
Thanks.
If you wanted a contatenated string of those values like "a b"
XElement e = XElement.Parse(xmlString);
var q = from pa in e.Elements("ProductAttribute")
select new
{
Id = (int)pa.Attribute("id"),
Value = string.Join(" " ,
pa.Elements("ProductAttributeValue")
.Elements("Value")
.Select(x=>x.Value)
.ToArray())
};
XElement e = XElement.Parse(xmlString);
var q = from pa in e.Elements("ProductAttribute")
select new
{
Id = (int)pa.Attribute("id"),
Value = from pav in pa.Elements("ProductAttributeValue").Elements("Value") select pav.Value
};
Of course, Value will be an IEnumerable<string>.
Edit:
If you want the output to concat the Value elements into one string you can do this:
XElement e = XElement.Parse(xmlString);
var q = from pa in e.Elements("ProductAttribute")
select new
{
Id = (int)pa.Attribute("id"),
Value = string.Join(" ", (from pav in pa.Elements("ProductAttributeValue").Elements("Value")
select pav.Value).ToArray())
};
Then the output will be:
Id Value
1 a
2 a b

How to display data from XML file to ListView using LINQ to XML?

I'm having an xml file like
<Root>
<Child val1="1" val2="2"/>
<Child val1="1" val2="3"/>
<Child val1="2" val2="4"/>
</Root>
i need to display the data from the Xml file to a Listview like
(Added A to index value)
Now i'm using like
1.Stores the data in an XmlNodesList
2.Then iterate through the nodeslist and add the attribute value to the list view
Here i can not use Dictionary<String,String> as a temporary storage because there exist multiple keys with same name.
Is there any idea to do this using LINQ to XML.?
Without LINQ:
var doc = new System.Xml.XmlDocument();
doc.LoadXml(xml);
var nodes = doc.SelectNodes("Root/Child");
for (int i = 0; i < nodes.Count; i++)
{
var n = nodes[i];
var index = String.Format("A{0}", i + 1);
var column1 = n.Attributes["val1"].Value;
var column2 = n.Attributes["val1"].Value;
// use variables to add an item to ListView
}
Using LINQ:
using System.Linq;
var doc = new System.Xml.XmlDocument();
doc.LoadXml(xml);
var nodes = doc.SelectNodes("Root/Child");
var arr = nodes
.OfType<XmlNode>()
.ToArray();
var result = arr
.Select(n =>
new
{
ClNo = String.Format("A{0}", Array.IndexOf(arr, n) +1),
Val1 = n.Attributes["val1"].Value,
Val2 = n.Attributes["val2"].Value,
});
ListView list = new ListView();
ListViewItem[] items = result
.Select(r => new ListViewItem(new[] { r.ClNo, r.Val1, r.Val2 })
.ToArray();
list.Items.AddRange(items);

Categories

Resources