Querying XML document using Linq - c#

I am trying to Linq an xml document, I am unable to query the inner elements as you can see from below code what I am trying to do. I want to get all records which has a certain name... Please help.
<?xml version="1.0" encoding="utf-8" ?>
<Student>
<Person name="John" city="Auckland" country="NZ" />
<Person>
<Course>GDICT-CN</Course>
<Level>7</Level>
<Credit>120</Credit>
<Date>129971035565221298</Date>
</Person>
<Person>
<Course>GDICT-CN</Course>
<Level>7</Level>
<Credit>120</Credit>
<Date>129971036040828501</Date>
</Person>
</Student>
And now the source
class Program
{
static void Main(string[] args)
{
string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
XDocument xDoc = XDocument.Load(path + "\\Student Data\\data.xml");
IEnumerable<XElement> rows =
from row in xDoc.Descendants("Person")
where (string)row.Attribute("Course") == "GDICT-CN"
select row;
foreach(XElement xEle in rows)
{
IEnumerable<XAttribute>attlist =
from att in xEle.DescendantsAndSelf().Attributes()
select att;
foreach(XAttribute xatt in attlist)
{
Console.WriteLine(xatt);
}
foreach (XElement elemnt in xEle.Descendants())
{
Console.WriteLine(elemnt.Value);
}
Console.WriteLine("-------------------------------------------");
}
Console.ReadLine();
}
}

Replace your LINQ where query with this one -
IEnumerable<XElement> rows = xDoc.Descendants().Where(d => d.Name == "Person"
&& d.Descendants().Any(e => e.Name == "Course"
&& e.Value == "GDICT-CN"));

Related

How to Mask or Replace the values in XML?

I have this XML:
<?xml version="1.0" encoding="UTF-8"?>
<TXLife xmlns="http://ACORD.org/Standards/Life/2" Version="2.22.00">
<TXLifeRequest>
<OLifE Version="2.22.0">
<Party id="BEB7-BDDC43FE3F01_10004">
<PartyTypeCode tc="1">PT_PERSON</PartyTypeCode>
<FullName>Gump,Forrest</FullName>
<ResidenceState tc="58">USA_WI</ResidenceState>
<Person id="D7329BB530E8_10304">
<FirstName>Forrest</FirstName>
<LastName>Gump</LastName>
</Person>
</Party>
</OLifE>
</TXLifeRequest>
</TXLife>
I want Replace the FirstName value to No Name.
This is related to Data Masking.
I'm trying to access the node with the given code below, but it is not working.
var testXML = XDocument.Load("C:\\DataMask\\P0500015703691806181259345440127.xml");
var nodePTCode = testXML.Descendants("Party").FirstOrDefault(cd => cd.Element("PartyTypeCode").Value == "PT_PERSON");
The namespace has to be included when addressing the elements.
Also for an XDocument you have to start from its Root property.
XDocument xdoc = XDocument.Load("C:\\DataMask\\P0500015703691806181259345440127.xml");
XNamespace ns = "http://ACORD.org/Standards/Life/2";
XElement firstName = xdoc.Root.Descendants(ns + "FirstName").FirstOrDefault();
if (firstName != null) { firstName.Value = "No Name"; }
The xml will have the update applied:
<TXLife xmlns="http://ACORD.org/Standards/Life/2" Version="2.22.00">
<TXLifeRequest>
<OLifE Version="2.22.0">
<Party id="BEB7-BDDC43FE3F01_10004">
<PartyTypeCode tc="1">PT_PERSON</PartyTypeCode>
<FullName>Gump,Forrest</FullName>
<ResidenceState tc="58">USA_WI</ResidenceState>
<Person id="D7329BB530E8_10304">
<FirstName>No Name</FirstName>
<LastName>Gump</LastName>
</Person>
</Party>
</OLifE>
</TXLifeRequest>
</TXLife>
Edit
If applicable, you'll also have to update the FullName xml element to reflect the change in FirstName.
XElement lastName = xdoc.Root.Descendants(ns + "LastName").FirstOrDefault();
XElement fullName = xdoc.Root.Descendants(ns + "FullName").FirstOrDefault();
fullName.Value = String.Format("{0},{1}", (String)lastName, (String)firstName);

Getting specific data in node by other data in same node

I got XML like that:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!--Some comment-->
<Databook>
<Note>
<Name>Camera2 made a snapshoot #243</Name>
<Value>Camera2_snapshoot-2013-09-06_21-47-35.png</Value>
</Note>
<Note>
<Name>Camera1 made a snapshoot #244</Name>
<Value>Camera1_snapshoot-2013-09-06_21-47-39.png</Value>
</Note>
</Databook>
And i want to get string beetwen [Value]..[/Value] of specific node, knowing only it's string of [Name]..[/Name].
This what i did so far:
string xmlfile = string.Format("XML/Diary/" + day);
XDocument dailyXML = XDocument.Load(xmlfile);
XElement Contact = (from xml2 in dailyXML.Descendants("Note")
where xml2.Element("Name").Value == item
select xml2).FirstOrDefault();
You are very close, if you just want the value of Value where the Name equals item
Try:
string result = (from xml2 in dailyXML.Descendants("Note")
where xml2.Element("Name").Value == item
select xml2.Element("Value").Value).FirstOrDefault();
or
string result = dailyXML.Descendants("Note")
.Where(n => n.Element("Name").Value == item)
.FirstOrDefault(n => n.Element("Value").Value);

Count people by gender

I need some help with C# and XML.
I'm building a web application and need to present information about number of peope with gender Male in a specific department (How many men in Dep1).
This is my XML file:
<company>
<department>
<departmentname>Dep 1</departmentname>
<people>
<person>
<name>Sean</name>
<date>2013-10-10</date>
<gender>male</gender>
<age>40</age>
</person>
<person>
<name>John</name>
<date>2013-10-18</date>
<gender>male</gender>
<age>45</age>
</person>
<person>
<name>Linda</name>
<date>2013-09-10</date>
<gender>female</gender>
<age>42</age>
</person>
<person>
<name>Bob</name>
<date>2013-10-01</date>
<gender>male</gender>
<age>35</age>
</person>
</people>
</department>
<department>
<departmentname>Dep 2</departmentname>
<people>
<person>
<name>Art</name>
<date>2013-09-10</date>
<gender>male</gender>
<age>38</age>
</person>
<person>
<name>Christina</name>
<date>2013-10-20</date>
<gender>female</gender>
<age>45</age>
</person>
<person>
<name>Marie</name>
<date>2013-09-10</date>
<gender>female</gender>
<age>49</age>
</person>
</people>
</department>
</company>
My code (not finished and not working):
XElement company= XElement.Load(Server.MapPath("myXML.xml"));
XElement department= (from p in company.Elements("department")
where p.Element("departmentname").Value == 'Dep 1' && p.Element
("gender").Value == 'male'
select p).Count;
numberTextBox.Text = department.Element; //???How to write to textbox
var department = (from p in company.Elements("department")
where p.Element("departmentname").Value == "Dep 1" && p.Element
("gender").Value == "male"
select p).Count();
You're not retrieving an XElement, you're retrieving an int (which is what Count returns).
Using var will infer this for you.
And per my comment: kon -> gender.
Let's separate out some of the elements of the search to make it easier to follow:
XElement depName = company.Descendants("departmentname")
.Where(x => x.Value == "Dep 1")
.FirstOrDefault();
XElement[] men = depName.Parent.Descendants("gender")
.Where(x => x.Value == "male")
.Where(x => (int)x.Parent.Element("age") > 40)
.ToArray();
int count = men.Length;
depName.Parent is the department element. From that we get all the gender's with the value male. So the variable men is only the genders. If you want the whole person element, use the men's Parent property to get that. For example:
foreach(XElement man in men)
Console.Writeline(man.Parent.Element("name").Value);
To assign to the textbox:
numberTextBox.Text = count.ToString(); // or men.Length.ToString()
Hope this will help you..
XmlDocument doc = new XmlDocument();
doc.Load(your document);
var items = doc.GetElementsByTagName("gender");
var x=items.Count;
int malecount=doc.SelectNodes("gender[. = \"male\"]").Count;
You could try just XMLDocument and Xpath, just use the following code
var xpath = "/company/department[departmentname='Dep 1']/people/person[/company/department/people/person/gender='male']/name";
var xmldoc = new XmlDocument();
xmldoc.Load("company.xml");
var result = xmldoc.SelectNodes(xpath);
if (result != null) Assert.AreEqual(4,result.Count);
Hope this helps.

XML/C#: Read content if only if attribute is correct

I have an XML file as follows, and I'm trying to read the content of the Name tag, only if the attribute of the Record tag is what I want. (continued below code)
The XML file is:
<?xml version="1.0" encoding="utf-8" ?>
<Database>
<Record Number="1">
<Name>John Doe</Name>
<Position>1</Position>
<HoursWorked>290</HoursWorked>
<LastMonthChecked>0310</LastMonthChecked>
</Record>
<Record Number="2">
<Name>Jane Doe</Name>
<Position>1</Position>
<HoursWorked>251</HoursWorked>
<LastMonthChecked>0310</LastMonthChecked>
</Record>
</Database>
This is the C# code I have so far:
public static string GetName(int EmployeeNumber)
{
XmlTextReader DataReader = new XmlTextReader("Database.xml");
DataReader.MoveToContent();
while (DataReader.Read())
{
if (DataReader.NodeType == XmlNodeType.Element
&& DataReader.HasAttributes && DataReader.Name == "Record")
{
DataReader.MoveToAttribute(EmployeeNumber);
DataReader.MoveToContent();
if (DataReader.NodeType == XmlNodeType.Element
&& DataReader.Name == "Name")
{
return DataReader.ReadContentAsString();
}
}
}
}
So for example, if 2 is passed to the function, I want it to return the string "Jane Doe". I'm new to XML parsing, so any help would be appreciated.
Thanks.
Use XPath for this.
Check this article: http://www.developer.com/xml/article.php/3383961/NET-and-XML-XPath-Queries.htm. It has an example that is very similar to your situation.
Could you try something like this:-
public static string GetName(int EmployeeNumber)
{
XmlDocument doc = new XmlDocument();
doc.Load("Database.xml");
XmlElement rootNode = doc.DocumentElement;
String query ="Record[#Number='"+EmployeeNumber.ToString()+"']/Name";
XmlNode data= rootNode.SelectSingleNode(query);
return data.InnerText;
}
You could use XPath if your XML is not very large:
using System;
using System.Xml.Linq;
using System.Xml.XPath;
public class Program
{
static void Main(string[] args)
{
var elements = XDocument.Load("Database.xml")
.XPathSelectElements("//Record[#Number='2']/Name");
foreach (var item in elements)
{
Console.WriteLine(item.Value);
}
}
}
string searchTerm = "2";
var list = from XElement segment in workingXmlDocument.Descendants(wix + "File")
where segment.Attribute("Id").Value.ToString() == searchTerm
select segment.Descendant("Name").Value;
That is a LINQ statement that will give you the name based on the variable searchTerm.

how to get root node attribute value using linq

I have the following XML. How to read the root node attribite value and it's decendents using LINQ? I am trying to read "dId" and "dTime" from root node, "id" from Customer element and Order number.
<?xml version="1.0" encoding="utf-8" ?>
<Customers dId="wqwx" dTime="10-9-09 11:23">
<Customer id="1">
<Orders>
<Order number="22" status="ok">
</Orders>
</Customer>
</Customers>
I tried the following code but it doesn't work.
XDocument doc= XDocument.Load(#"C:\Customers.xml");
var q = from c in doc.Descendants("Customers")
select new
{
dID = c.Attribute("dId"),
dTime = c.Attribute("dTime");
}
first, fix your xml (<Order .... />)
then, your linq should look like this....
// .Elements(...) selects all elements of type "Customer"
var q = from c in xDoc.Elements("Customers")
select new
{
dID = c.Attribute("dId"),
dTime = c.Attribute("dTime")
};
you should dl LinqPad... it lets you do Linq queries on the fly, even agains SQL databases. Then, once you get the results you want, copy and past your linq into your source code.
You have to end the order tag with: />
xDoc.Descendants("Customers") should work as well as xDoc.Elements("Customers").
Chris, is there a specific advantage to using .Elements?
You can't use LINQ to access the root tag.
The code below does what you want (I included a well formed xml file as well):
using System;
using System.Linq;
using System.Xml.Linq;
namespace ReadXmlSpike
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Reading file...");
XDocument doc = XDocument.Load("Customers.xml");
var customers =
new
{
DID = (string) doc.Element("Customers").Attribute("did"),
DTime = (DateTime) doc.Element("Customers").Attribute("dTime"),
Customers = from customerxml in doc.Descendants("Customer")
select
new
{
ID = (string)customerxml.Attribute("id"),
Orders = from orderxml in customerxml.Descendants("Order")
select
new
{
Number =(string) orderxml.Attribute("number")
}
}
};
Console.WriteLine("Customersfile with id: {0} and time {1}",customers.DID,customers.DTime);
foreach (var customer in customers.Customers)
{
Console.WriteLine("Customer with id {0} has the following orders:",customer.ID);
foreach (var order in customer.Orders)
{
Console.WriteLine("Order with number {0}",order.Number);
}
}
Console.ReadLine();
}
}
}
and the xml file:
<?xml version="1.0" encoding="utf-8" ?>
<Customers did="www" dTime="10-09-09 11:23">
<Customer id="1">
<Orders>
<Order number="22" status="ok"/>
<Order number="23" status="bad"/>
</Orders>
</Customer>
<Customer id="2">
<Orders>
<Order number="24" status="ok"/>
<Order number="25" status="bad"/>
</Orders>
</Customer>
</Customers>
XDocument d = XDocument.Parse(#"<?xml version='1.0' encoding='utf-8' ?>
<Customers dId='wqwx' dTime='10-9-09 11:23'>
<Customer id='1'>
<Orders>
<Order number='22' status='ok'/>
</Orders>
</Customer>
</Customers>");
var cu = d.Root.Elements().Where(n => n.Name == "Customer");
var c = from cc in cu
select new
{
dId = cc.Document.Root.Attribute("dId").Value,
dTime = cc.Document.Root.Attribute("dTime").Value,
ID = cc.Attribute("id").Value,
number = cc.Element("Orders").Element("Order").Attribute("number").Value
};
foreach (var v in c)
{
Console.WriteLine("dId \t\t= {0}", v.dId);
Console.WriteLine("dTime \t\t= {0}", v.dTime);
Console.WriteLine("CustomerID \t= {0}", v.ID);
Console.WriteLine("OrderCount \t= {0}", v.number);
}
Console Output:
================================
dId = wqwx
dTime = 10-9-09 11:23
CustomerID = 1
OrderCount = 22
请按任意键继续. . .
It does not work the way you wrote it: while printing the above code would complain about anonymous type.
However, with this simple modified version d.Document.Root.Attribute("dId").Value; you can assign it to a string.

Categories

Resources