Linq to XML casting XML file to custom object - c#

I'm using Linq to XML to read in an XML file and as part of this I'd like to create an object. My object looks like this:
public class Address
{
public string AccountRef { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
// more stuff here
}
And my XML file looks like this:
<rows>
<row>
<FIELD NAME="AccountRef">1234</FIELD>
<FIELD NAME="AddressLine1">My Address Line 1</FIELD>
<FIELD NAME="AddressLine2">My Address Line 2</FIELD>
</row>
<row>
<FIELD NAME="AccountRef">5678</FIELD>
<FIELD NAME="AddressLine1">My Address Line 3</FIELD>
<FIELD NAME="AddressLine2">My Address Line 4</FIELD>
</row>
</rows>
In terms of code, I've tried various things, but at present I have the following which returns the correct number of rows in the format:
<row><FIELD NAME="AccountRef">1234</FIELD><FIELD>...rest of data</row>
<row><FIELD NAME="AccountRef">5678</FIELD><FIELD>...rest of data</row>
The code that does this is:
var results = (from d in document.Descendants("row")
select d).ToList();
So basically what I'm trying to do is something like:
var results = (from d in document.Descendants("row")
select new Address
{
AccountRef = d.Attribute("AccountRef").Value,
AddressLine1 = d.Attribute("AddressLine1").Value
}).ToList();
Obviously because my nodes are the same (FIELD NAME) that won't work, so does anyone have an idea how I can achieve this?

you need to retrive field names and values before creating objects
var results = document.Descendants("row")
.Select(row=>row.Elements("FIELD").ToDictionary(x=>x.Attribute("NAME").Value, x=>x.Value))
.Select(d=>new Address
{
AccountRef = d["AccountRef"],
AddressLine1 = d["AddressLine1"],
AddressLine2 = d["AddressLine2"],
});
check demo

Related

Linq to XML query analyz

This query does work but I am not sure it is proper way to write this kind of query. I feel it is using too many Descendants and Parent.
Is there a better way to write this query?
There can be more than one catalog in XML.
static IEnumerable<Parts> GetAllParts(XDocument doc, string catalog, string groupId, string subGroupId)
{
var parts = (from p in doc.Descendants("ROOT").Descendants("CATALOG").Descendants("GROUP").Descendants("SUBGROUP").Descendants("BOM").Descendants("PARTS")
where (string)p.Parent.Parent.Parent.Parent.Element("IDENT").Value == catalog
&& p.Parent.Parent.Parent.Element("IDENT").Value == groupId
&& p.Parent.Parent.Element("IDENT").Value == subGroupId
select new Parts
{
ObjectId = int.Parse(p.Attribute("OBJECTID").Value),
Ident = p.Element("IDENT").Value,
List = p.Element("LIST").Value,
Descr = p.Element("DESC").Value
});
return parts;
}
}
public class Parts
{
public int ObjectId { get; set;}
public string Descr { get; set; }
public string Ident { get; set; }
public string List { get; set; }
}
Update: XML added.
<ROOT>
<CATALOG>
<OBJECT_ID>001</OBJECT_ID>
<OBJECT_IDENT>X001</OBJECT_IDENT>
<GROUP>
<OBJECT_ID>1001</OBJECT_ID>
<OBJECT_IDENT>01</OBJECT_IDENT>
<NAME>HOUSING</NAME>
<SUBGROUP>
<OBJECT_ID>5001</OBJECT_ID>
<OBJECT_IDENT>01.05</OBJECT_IDENT>
<NAME>DESIGN GROUP 1</NAME>
<BOM>
<OBJECT_ID>6001</OBJECT_ID>
<OBJECT_IDENT>010471</OBJECT_IDENT>
<PARTS>
<OBJECT_ID>2316673</OBJECT_ID>
<OBJECT_IDENT>A002010660</OBJECT_IDENT>
<DESC>SHORT BLOCK</DESC>
<NOTES>
<ROW>
<NOTES>Note 1</NOTES>
<BOM>010471</BOM>
<POS>1</POS>
</ROW>
<ROW>
<NOTES>Note 2</NOTES>
<BOM>010471</BOM>
<POS>2</POS>
</ROW>
</NOTES>
</PARTS>
<PARTS>
</PARTS>
<PARTS>
</PARTS>
</BOM>
</SUBGROUP>
<SUBGROUP>
</SUBGROUP>
<SUBGROUP>
</SUBGROUP>
</GROUP>
<GROUP>
</GROUP>
</CATALOG>
</ROOT>
Some suggestions regarding your question (I hope you'll improve your future posts) :
Your code and the XML posted doesn't work. For instance, the XML has <OBJECT_IDENT> element, but your LINQ has IDENT. Please craft it carefully and make sure it does work to avoid confusion.
Please put some effort in explaining the problem and giving clarification. "I am retrieving Parts data as function name says" is not clear enough as simply getting <Parts> elements doesn't need filtering but your LINQ has where .... clause.
This question seems to suits better in https://codereview.stackexchange.com/
And here is some suggestions regarding your code :
Use .Elements() to get direct child of current node as opposed to Descendants() which get all descendat nodes.
You can use Elements().Where() to filter the element so you can avoid traversing Parents
You can cast XElement to string/int to avoid exception in case such element not found
Example code snippet :
var parts = (from p in doc.Root
.Elements("CATALOG").Where(o => catalog == (string)o.Element("OBJECT_IDENT"))
.Elements("GROUP").Where(o => groupId == (string)o.Element("OBJECT_IDENT"))
.Elements("SUBGROUP").Where(o => subGroupId == (string)o.Element("OBJECT_IDENT"))
.Elements("BOM")
.Elements("PARTS")
select new Parts
{
ObjectId = (int)p.Element("OBJECT_ID"),
Ident = (string)p.Element("OBJECT_IDENT"),
List = (int)p.Element("LIST"),
Descr = (string)p.Element("DESC")
});

How to Search in XML and show results in GridView in C#?

I have an XML data of Employees, I want to parse this data and show it in Gridview using C# in ASP.NET.
Based on search parameters like Employee ID or Company ID or Department ID, I should be able to filter the data and update the GridView.
Checked few links in internet, but nothing matches this particular format.. Is it possible to achieve.. (Any links to) code will be helpful.
<?xml version="1.0" encoding="utf-8"?>
<Employees>
<Employee>
<Id> TG18-2002</Id>
<Name> AAPM^Test^Patterns</Name>
<Sex> O </Sex>
<Company>
<Id> 2.16</Id>
<Department>
<Id> 2.16.124</Id>
<Project>
<Id> 2.16.124.113543</Id>
</Project>
</Department>
</Company>
</Employee>
<Employee>
<ID> TG18-2003</ID>
<Name> AAPM^Test^Patt</Name>
<Sex> O </Sex>
<Company>
<ID> 2.16</ID>
<Department>
<ID> 2.16.124</ID>
<Project>
<ID> 2.16.124.113543</ID>
</Project>
</Department>
</Company>
</Employee>
<Employee>
</Employees>
Note: I am trying to build something like, this
Looking at the link you referenced, I think you're best off creating an Employee class and filling this with the XML data.
This will allow you to de-couple your data (in this case XML, but could be anything) from your view (in this case ASP.NET Web Forms, but again could be anything), which I find handy and seems to be common these days (MVVM etc..).
Another benefit is that you can turn a nested data source like XML into something a little flatter to make your view binding simpler, for example in the example you provided you could make the Company fields available as properties of the outer Employee class.
Yet another benefit is that if/when your view becomes responsive, then you can make properties of your view model observable, and therefore allow updates to the model, immediately update your view.
All that said, here is a small snippet showing your Employee class, and filling it from the XML sample you provided. I am using Linq to XML, but there are so many ways you could do this (adapters, readers, navigators...).
I think the important thing here is that you de-couple your data from the view.
class Employee
{
public string Id { get; set; }
public string Name { get; set; }
public string Sex { get; set; }
public Company Company { get; set; }
}
class Company
{
public string Id { get; set; }
public Department Department { get; set; }
}
class Department
{
public string Id { get; set; }
public Project Project { get; set; }
}
class Project
{
public string Id { get; set; }
}
var xml = #"<?xml version='1.0' encoding='utf-8'?>
<Employees>
<Employee>
<Id> TG18-2002</Id>
<Name> AAPM^Test^Patterns</Name>
<Sex> O </Sex>
<Company>
<Id> 2.16</Id>
<Department>
<Id> 2.16.124</Id>
<Project>
<Id> 2.16.124.113543</Id>
</Project>
</Department>
</Company>
</Employee>
<Employee>
<Id> TG18-2003</Id>
<Name> AAPM^Test^Patt</Name>
<Sex> O </Sex>
<Company>
<Id> 2.16</Id>
<Department>
<Id> 2.16.124</Id>
<Project>
<Id> 2.16.124.113543</Id>
</Project>
</Department>
</Company>
</Employee>
</Employees>
";
// read the xml into the class
var doc = XDocument.Parse(xml);
var data = (from row in doc.Root.Elements("Employee")
let company = row.Element("Company")
let dept = company.Element("Department")
let project = dept.Element("Project")
select new Employee
{
Id = row.Element("Id").Value.Trim(),
Name = row.Element("Name").Value.Trim(),
Sex = row.Element("Sex").Value.Trim(),
Company = new Company
{
Id = company.Element("Id").Value.Trim(),
Department = new Department
{
Id = dept.Element("Id").Value.Trim(),
Project = new Project
{
Id = project.Element("Id").Value.Trim()
}
}
}
});
// now you have a collection of 'employees', bind them..
Once we're that far, you need to decide how you want to query your data. The answer to this is as usual, it depends, and in this case I think it depends mostly on the size of the XML data you have and if it makes sense to bring this into memory or not.
If you can bring it into memory, then a collection of Employees is nice and simple to query using Linq and I would recommend this approach.
If the XML is large, then you will probably need to use an XMLReader to build your class. A little more complex, but the result should still be that you bind your grid to your class and keep the XML separate from the view.
DataSet xmlData = new DataSet();
xmlData.ReadXml(YourXMLPath);
GridviewControl1.DataSource = xmlData.Tables[0];
as far my understanding you need to do maipulication in data before showing it in Grid Control.
for that i recommend you to use LINQ query. you can manipulicate the things at client side with queries.
below i trying to solve the prob.
DataSet xmlData = new DataSet();
xmlData.ReadXml(YourXMLPath);
DataTable dt =From x in xmlData.Tables[0].AsEnumerable().Where(x=>x.Field<string>("Name").StartWith("A")) Selct(x=>x).CopytodDataTable;
now you can use "dt" for your data grid
xmlData.Tables[0]= dt;
GridviewControl1.DataSource = xmlData.Tables[0];
GridviewControl1.DataBind();//this line required if it is for asp.net
Hope it will helps you. :)

LINQ to XML via C#

I'm new to LINQ. I understand it's purpose. But I can't quite figure it out. I have an XML set that looks like the following:
<Results>
<Result>
<ID>1</ID>
<Name>John Smith</Name>
<EmailAddress>john#example.com</EmailAddress>
</Result>
<Result>
<ID>2</ID>
<Name>Bill Young</Name>
<EmailAddress>bill#example.com</EmailAddress>
</Result>
</Results>
I have loaded this XML into an XDocument as such:
string xmlText = GetXML();
XDocument xml = XDocument.Parse(xmlText);
Now, I'm trying to get the results into POCO format. In an effort to do this, I'm currently using:
var objects = from results in xml.Descendants("Results")
select new Results
// I'm stuck
How do I get a collection of Result elements via LINQ? I'm particularly confused about navigating the XML structure at this point in my code.
Thank you!
This will return a IEnumerable of anonymous class:
var q = from result in xml.Descendants
select new
{
ID = result.Descendants("ID"),
Name= result.Descendants("Name"),
EmailAddress= result.Descendants("EmailAddress")
};
or if you have defined class `Result, e.g.:
class Result
{
public ID { get; set; }
public Name { get; set; }
public EmailAddress { get; set; }
}
then:
var q = from result in xml.Descendants
select new Result
{
ID = result.Descendants("ID"),
Name = result.Descendants("Name"),
EmailAddress = result.Descendants("EmailAddress")
};
(returns IEnumerable<Result>)
If your Results child elements are only Result elements, then you can get them like this:
var objects = from result in xml.Descendants
select result;
But in this lucky case you can just use xml.Descendants.
If it's not only Result elements, then this will do fine:
var object = from result in xml.Descendants
where result.Name == "Result"
select result;

C# linq-to-xml, Getting a list with nodes?

This is the test xml that i am using:
<categories>
<category id="1" name="Test1">
<category id="2" name="Test2">
<misc id="1"></misc>
</category>
</category>
<category id="3" name="Test3">
<misc id="2"></misc>
</category>
Now i want to bind that to an ASPX treeview, i want only the elements that have the name category and i want the name of those to appear in the treeview.
Its easy to get the id and names:
var d = from t in data.Descendants("category")
select new { ID = t.Attribute("id").Value, Name = t.Attribute("name").Value };
but how do i keep the structure in the treeview?
This should look like this:
Test1
-> Test2
Test3
Maybe something like this if I understand you correctly? (I have not tested it)
class Category
{
public string ID { get; set; }
public string Name { get; set; }
public IEnumerable<Category> SubCategories { get; set; }
}
IEnumerable<Category> CategoryTreeStructure(XElement e)
{
var d = from t in e.Elements("category")
select new Category()
{
ID = t.Attribute("id").Value,
Name = t.Attribute("name").Value,
SubCategories = CategoryTreeStructure(t)
};
return d;
}
Call it with:
var structure = CategoryTreeStructure(doc.Root);
"i want only the elements that have the name category" - I do not understand what you mean here? But if you only want to select those elements which have a "name" attribute then this should work:
...
var d = from t in e.Elements("category")
where t.Attribute("name") != null
select new Category()
...
I understand that the upper (the "name" attribute part) is not what you wanted but I leave it there. I have tested the code against:
XDocument doc = XDocument.Parse(#"<categories>
<category id=""1"" name=""Test1"">
<category id=""2"" name=""Test2"">
<misc id=""1""></misc>
</category>
</category>
<category id=""3"" name=""Test3"">
<misc id=""2""></misc>
</category>
</categories>");
var structure = CategoryTreeStructure(doc.Root);
Actually, I have found this link which does exactly what you are asking for :) And it is without LINQ, so I thought it deserved another answer.
http://www.15seconds.com/issue/041117.htm

XML with C# LINQ to XML

Following is the example of an XML document.
<People>
<Person>
<Name>ABC </Name>
<SSN>111111</SSN>
<Address>asdfg</Address>
</Person>
</People>
I need to get the tag names but not the values between the tag names. That is, under the person tag, I should grab the Name, SSN, and Address nodes and not the ABC, 111111, and asdfg values.
I need to use LINQ to XML and query it in C#.
How can I do it?
This returns the names as a list of strings:
var doc = XDocument.Parse(#"<People>
<Person>
<Name>ABC </Name>
<SSN>111111</SSN>
<Address>asdfg</Address>
</Person>
</People>"
);
var list = doc.Root.Element("Person").Descendants()
.Select(node => node.Name.LocalName).ToList();
In case you're using an XElement instead of an XDocument, you can remove the .Root from the above code and get the correct results.
Create a class
public class Person
{
public string Name {get; set;}
public int SSN {get; set;}
public string Address {get; set;}
}
And create a new person this way;
List<Person> NewPersons = new List<Person>();
XDocument doc = XDocument.Load(xmlFile);
foreach(XElement xElem in doc.Descendants("Person"))
{
NewPersons.Add(new Person
{
Name = xElem. Element("Name").Value,
SSN = xElem.Element("SSN").Value,
Address = xElem.Element("Address").Value,
});
}
You can get it this way...
string xml = "<People> <Person> <Name>ABC </Name> <SSN>111111</SSN> <Address>asdfg</Address> </Person> <Person> <Name>ABC </Name> <SSN>111111</SSN> <Address>asdfg</Address> </Person> </People>";
XElement xmlData = XElement.Parse(xml);
foreach(XElement xmlPerson in xmlData.Elements("Person"))
{
List<string> TagsForThisPerson = new List<string>();
foreach(XElement xmlTag in xmlPerson.Elements())
{
TagsForThisPerson.Add(xmlTag.Name.ToString());
}
TagsForThisPerson.Dump();
}
Simple LINQ syntax to get the names is listed below. It assumes you have the XML loaded in a XDocument variable named doc.
var nodeNames = from node in doc.Descendants("Person").First().Descendants()
select node.Name.LocalName;
It only looks at the first person. If you have more than one in the XML document, the list of names is probably not what you would want (no reason to repeat all the names of the nodes over and over). This way, you get a list of just the node names for the first person, but it does assume that the first one would have a complete list of names. If they vary, you would need to build a distinct list from all the nodes.

Categories

Resources