Count people by gender - c#

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.

Related

Add XElement to list and have it's children be remainder of list

If I have the following xml:
<Employees>
<Person>
<ID>1000</ID>
<Name>Nima</Name>
<LName>Agha</LName>
</Person>
<Person>
<ID>1002</ID>
<Name>Ligha</Name>
<LName>Ligha</LName>
</Person>
<Person>
<ID>1003</ID>
<Name>Jigha</Name>
<LName>Jigha</LName>
</Person>
</Employees>
I want to add a new node, after an indexed node, and add the remaining person nodes as the children of this new node.
So the new xml after adding would look like this:
<Employees>
<Person>
<ID>1000</ID>
<Name>Nima</Name>
<LName>Agha</LName>
</Person>
<RefNode>
<Person>
<ID>1002</ID>
<Name>Ligha</Name>
<LName>Ligha</LName>
</Person>
<Person>
<ID>1003</ID>
<Name>Jigha</Name>
<LName>Jigha</LName>
</Person>
</RefNode>
</Employees>
So far I have tried using
ElementAt(index).AddAfterSelf()
but that just adds it next in line, and doesn't add the next two Persons as children.
You could do as following. Have added comments in code for better understanding
var xdoc = XDocument.Parse(xml);
// Unclear how you are identifying the node after which the change has to happen. For sake of example, using ID
var selectedNode = xdoc.Descendants("Person")
.First(x=>Convert.ToInt32(x.Element("ID").Value)==1000);
// Collection of nodes that would be added as child of newly inserted node
var nodeAfterSelectedNode = xdoc.Descendants("Person")
.SkipWhile(x=>x==selectedNode)
.ToList();
// Create the new node with previously identified 'nodeAfterSelectedNode' as Children
var newElement = new XElement("RefNode",nodeAfterSelectedNode);
// Remove the existing Nodes (ones that are being moved to child)
foreach(var node in nodeAfterSelectedNode)
{
node.Remove();
}
// add the new node
selectedNode.AddAfterSelf(newElement);
var newXml = xdoc.ToString();
Output
<Employees>
<Person>
<ID>1000</ID>
<Name>Nima</Name>
<LName>Agha</LName>
</Person>
<RefNode>
<Person>
<ID>1002</ID>
<Name>Ligha</Name>
<LName>Ligha</LName>
</Person>
<Person>
<ID>1003</ID>
<Name>Jigha</Name>
<LName>Jigha</LName>
</Person>
</RefNode>
</Employees>
Output Sample

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);

XElement with LINQ Select and optional elements

I am trying to display some date from some XML I get from an external service. I am using XElement and I try to use LINQ select to get my data.
var xElem = XElement.Load(HttpUtility.UrlPathEncode(url));
var books = (from pubs in xElem.Elements("result")
select new
{
Id = (string)pubs.Element("data").Element("id"),
Title = (string)pubs.Element("data").Element("title"),
Year = (string)pubs.Element("data").Element("year"),
Resources = (string)pubs.Element("data")
.Element("resource")
.Element("url")
.ElementValueNull(),
Authors= pubs.Element("data").Elements("person")
}).ToList();
foreach (var book in books)
{
// Put the string together with string builder....
foreach (var person in book.Authors)
{
//Get the authors
}
}
And of course I have made the class for ElementValueNull.
//This method is to handle if element is missing
public static string ElementValueNull(this XElement element)
{
if (element != null)
return element.Value;
return "";
}
//This method is to handle if attribute is missing
public static string AttributeValueNull(this XElement element, string attributeName)
{
if (element == null)
return "";
else
{
XAttribute attr = element.Attribute(attributeName);
return attr == null ? "" : attr.Value;
}
}
The problem is that the resource tag with it's elements are not always present. And if it isn't there it will skip the whole record. Is there any easy way of making it so that it will just make the Resources have the empty string returned from my class but still add the record still using a LINQ select?
EDIT with XML example:
<?xml version="1.0" encoding="UTF-8"?>
<tester xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://tester.no/xmlSchema/xsd/tester.xsd">
<generert>2014-12-01</generert>
<result>
<data>
<id>297474</id>
<person>
<id>11690</id>
<surname>Medel-Svensson</surname>
<firstname>Ronnie</firstname>
</person>
<title>Title 1</title>
<year>2009</year>
</data>
</result>
<result>
<data>
<id>807059</id>
<person>
<id>11690</id>
<surname>Bronskimlet</surname>
<firstname>Hallstein</firstname>
</person>
<person>
<id>328009</id>
<surname>Kroksleiven</surname>
<firstname>Jostein</firstname>
</person>
<person>
<id>328010</id>
<surname>Gassolini</surname>
<firstname>Ruffino</firstname>
</person>
<person>
<id>327990</id>
<surname>von Schnellfahrer</surname>
<firstname>Heinrich</firstname>
</person>
<title>Title 2</title>
<year>2010</year>
<resource>
<type>
<code>TEXT</code>
</type>
<url>http://www.example.com/</url>
</resource>
</data>
</result>
<result>
<data>
<id>1164653</id>
<person>
<id>11690</id>
<surname>Bergsprekken</surname>
<firstname>Mysil</firstname>
</person>
<title>Title 3</title>
<year>2014</year>
<resource>
<type>
<code>FULLTEKST</code>
</type>
<url>http://www.example.com/</url>
</resource>
</data>
</result>
</tester>
A couple of things:
if you use Element(..), then the result could be null. This may cause null reference exceptions if elements are missing in your path. A more elegant way to handle this would be to use sequences and return an element if present using SingleOrDefault()
Both XElement and XAttribute have a bunch of explicit type conversion operators built in. This means you can cast to string and various other primitives. As string is a reference type, it would return null if the XObject was null. Value types such as int would throw an exception in this case, though int? would not.
With this in mind, something like this should solve your problem. Note as 'data' is common to all, you can put this in the initial selector:
from pubs in xElem.Elements("result").Elements("data")
select new
{
Id = (string)pubs.Element("id"),
Title = (string)pubs.Element("title"),
Year = (string)pubs.Element("year"),
Resources = (string)pubs.Elements("resource")
.Elements("url")
.SingleOrDefault(),
Authors= pubs.Elements("person")
}

Add XElement to another XElement in specific location

Please consider this XML:
<Employees>
<Person>
<ID>1000</ID>
<Name>Nima</Name>
<LName>Agha</LName>
</Person>
<Person>
<ID>1002</ID>
<Name>Ligha</Name>
<LName>Ligha</LName>
</Person>
<Person>
<ID>1003</ID>
<Name>Jigha</Name>
<LName>Jigha</LName>
</Person>
</Employees>
That is content of a XElement variable.Now I have another XElement variable with this content:
<Person>
<ID>1001</ID>
<Name>Aba</Name>
<LName>Aba</LName>
</Person>
I want to add this XEelemnt variable to first XElement in a specific position (for example as second Person tag). How I can do this?
thanks
First you need to load the xml string, second you get the position where you want to insert the xml, then insert the new xml. Here is an example how to do it.
var reader = new StringReader(#"<Employees>
<Person>
<ID>1000</ID>
<Name>Nima</Name>
<LName>Agha</LName>
</Person>
<Person>
<ID>1002</ID>
<Name>Ligha</Name>
<LName>Ligha</LName>
</Person>
<Person>
<ID>1003</ID>
<Name>Jigha</Name>
<LName>Jigha</LName>
</Person>
</Employees>");
var xdoc = XDocument.Load(reader);
xdoc.Element("Employees").
Elements("Person").
First().
AddAfterSelf(new XElement("Person",
new XElement("ID", 1001),
new XElement("Name", "Aba"),
new XElement("LName", "Aba")));
var sb = new StringBuilder();
var writer = new StringWriter(sb);
xdoc.Save(writer);
Console.WriteLine(sb);
UPDATE
If you want to insert by index, just get the element first. For example you want to insert as second position, then you need to get the first index (index = 0).
var xdoc = XDocument.Load(reader);
xdoc.Element("Employees").
Elements("Person").
ElementAt(0).
AddAfterSelf(new XElement("Person",
new XElement("ID", 1001),
new XElement("Name", "Aba"),
new XElement("LName", "Aba")));
PS: For simplicity purpose I didn't add nullity check.

Querying XML document using Linq

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"));

Categories

Resources