XDocument Descendants of Descendants - c#

<Tickets>
<Extract_Date>2011-02-25 00:00:00</Extract_Date>
<Incidents>
<Ticket xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Ticket_Number>INC000000578057</Ticket_Number>
<Status>
<Value>Cancelled</Value>
<Reason>Cancelled by user</Reason>
</Status>
</Ticket>
I can get ticket_number OK with:
var q1 = from c in xmlDoc.Descendants("Ticket")
select new
{
Ticket_Number = (string)c.Element("Ticket_Number"),
};
How to get Reason also?

This should work:
var q1 = from c in xmlDoc.Descendants("Ticket")
select new
{
Ticket_Number = (string)c.Element("Ticket_Number"),
Reason = (string)c.Element("Status").Element("Reason")
};

//if you have exactly one <Ticket> with exactly one <Reason>
string strReason = xmlDoc.Descendants("Ticket").Single()
.Descendants("Reason").Single().Value;
//if you have one or multiple <Ticket> elements,
//each with exactly one <Reason> element
string[] astrReasons = xmlDoc.Descendants("Ticket")
.Select(ticket => ticket.Descendants("Reason").Single().Value).ToArray();
//if you have one or multiple <Ticket> elements,
//each with one or multiple <Reason> elements
string[] astrReasons2 = xmlDoc.Descendants("Ticket")
.SelectMany(ticket => ticket.Descendants("Reason")
.Select(reason => reason.Value)).ToArray();

Related

How to get different values with same name from xml with linq

XML - Code:
<Store>
<Products>
<Product id="PROD01">
<Title>Product 1</Title>
<Description><![CDATA[Product <b>1</b> description]]></Description>
<Image>prod01.gif</Image>
<Specs>
<Spec>Good computer</Spec>
<Spec>Good display</Spec>
<Spec>Latest version</Spec>
</Specs>
<Availability>same day</Availability>
</Product>
<Product id="PROD02">
<Title>Product 2</Title>
<Description><![CDATA[Product <b>2</b> description]]></Description>
<Image>prod01.gif</Image>
<Specs>
<Spec>Good computer</Spec>
<Spec>Soon available</Spec>
</Specs>
<Availability>next day</Availability>
</Product>
</Products>
</Store>
C# - Code:
public List<DetailList> GetDetails()
{
DetailList d = new DetailList();
List<DetailList> DetailLists =
(from product in xdocList.Descendants("Product")
join detail in xdocDetail.Descendants("Product")
on (string)product.Attribute("id") equals (string)detail.Attribute("id")
into outerProducts
from outerProduct in outerProducts
select new DetailList
{
Detail1 = (string)product.Attribute("id"),
Detail2 = (string)product.Element("Title"),
Detail3 = (string)product.Element("Description"),
Detail4 = (string)product.Element("Image"),
Detail5 = (string)outerProduct.Elements("Specs")
Detail6 = (string)outerProduct.Element("Availability"),
Detail7 = (string)product.Element("Price"),
}).ToList();
return DetailLists;
}
Output: Good computerGood displayLatest version
But wanted output is:
Good computer
Good display
Latest version
For output I used asp:repeater. I tried to add tags like < b r/> and much more, but cant'find my mistake, how to get Spec to three different strings, not only one string. How to achive that?
I am not sure why you are joing the nodes with self, but as per your XML it is not required. You can simply project the elements like this:-
public static List<DetailList> GetDetails(XDocument xdocList)
{
DetailList d = new DetailList();
List<DetailList> DetailLists = (from product in xdocList.Descendants("Product")
select new DetailList
{
Detail1 = ((string)product.Attribute("id")),
Detail2 = ((string)product.Element("Title")),
Detail3 = ((string)product.Element("Description")),
Detail4 = ((string)product.Element("Image")),
Detail5 = product.Element("Specs")
.Elements("Spec")
.Select(x => (string)x).ToList(),
Detail6 = ((string)product.Element("Availability")),
Detail7 = ((string)product.Element("Price")),
}).ToList();
return DetailLists;
}
Since you need all the Specs separately, you should have a collection of string and not just string. So the datatype of property Detail5 should be:-
List<string> or string[]
Friend got solution from Rahul
Detail5 = String.Join("<br/>", outerProduct.Element("Specs").Elements("Spec").Select(x => (string)x).ToList()),
Thanks #Rahul Singh

How to retrieve multiple let linq to xml c#

I am trying to get all the Hoop attributes but its only getting the first values(In this instance it is adding 24 and 4 to the listbox). Is there a way of adding all my result to a list. I usually use .ToList() but it did not work in this instant. The aim was to get the hoops for Home and away separate then store it into an object.
XML:
<League>
<Round>
<Match>
<Team Side="Home" >
<Hoop qtr="1st" player-name="Joe" time-scored="24" />
<Hoop qtr="1st" player-name="Jack" time-scored="54" />
</Team>
<Team Side="Away">
<Hoop qtr="1st" player-name="James" time-scored="4" />
<Hoop qtr="1st" player-name="Brown" time-scored="34" />
</Team>
</Match>
</Round>
</League>
C#:
XDocument xDoc = XDocument.Load("test.xml");
var query = from q in xDoc.Descendants("Team")
where (string)q.Attribute("Side") == "Home"
let d = q.Element("Hoop")
select new
{
Period = d.Attribute("qtr").Value,
Name = d.Attribute("player-name").Value,
Time = d.Attribute("time-scored").Value
};
foreach (var qq in query)
{
listBox.Items.Add(qq.Time);
}
Change let d = q.Element("Hoop") to from d in q.Elements("Hoop")
var xDoc = XDocument.Load("test.xml");
var query = from q in xDoc.Descendants("Team")
where (string)q.Attribute("Side") == "Home"
from d in q.Elements("Hoop")
select new
{
Period = d.Attribute("qtr").Value,
Name = d.Attribute("player-name").Value,
Time = d.Attribute("time-scored").Value
};
foreach (var qq in query)
{
listBox.Items.Add(qq.Time);
}

C# XML linq query

Hi I have the following XML:
<EPICORTLOG>
<POS>
<ClientId>WkStn.90.1</ClientId>
<Id>POS.90.20140819.251.8279</Id>
<StartTime>2014-08-25T05:12:34</StartTime>
<Store>90</Store>
<SysDate>2014-08-19T00:00:00</SysDate>
<TillNo>1</TillNo>
<Time>2014-08-25T05:12:34</Time>
<Tran>1093</Tran>
<WkStn>1</WkStn>
<WORKSTATION>
<IsAutoLock>1</IsAutoLock>
</WORKSTATION>
<TRADE>
<ITEM>
<Class>102499</Class>
<Desc>NIKE RACER</Desc>
<FinalPrice>82.77</FinalPrice>
<Status>ACTV</Status>
<Style>EV0615</Style>
<Tag>1</Tag>
</ITEM>
</TRADE>
</POS>
</EPICORTLOG>
There are many POS nodes like above in the actual XML. I am trying to fetch the POS node with ID=POS.90.20140819.251.8279 and then the details of Item from that particular node. I have written the following query:
XDocument xdoc = XDocument.Load(XMLFile);
var item = from items in xdoc.Element("EPICORTLOG").Descendants("POS")
where items.Attribute("Id").Value == strSelectedPOSID
select new
{
desc=items.Element("ITEM").Attribute("Desc")
};
But it is not yielding any result for me. Here strSelectedPOSID=POS.90.20140819.251.8279. Please let me know where i am going wrong.
Id and Desc are not an Attributes. they are Elements so you should use
var item = from items in xdoc.Descendants("POS")
where (string)items.Element("Id") == strSelectedPOSID
select new
{
desc = (string)items.Element("ITEM").Element("Desc")
};
I got the value at last!! Following is what i used:
var item = from items in xdoc.Element("EPICORTLOG").Descendants("POS")
where (string)items.Element("Id") == strSelectedPOSID
select new
{
desc = items.Element("TRADE").Element("ITEM").Element("Desc").Value.ToString()
};
Thanks for the inputs.

XML parse nodes and subnodes with LINQ

I do have a XML similar like this
<?xml version="1.0" encoding="UTF-8"?>
<e_schema>
<schema_name value="shema1">
<contact>
<id>1</id>
<firstName>firstname1</firstName>
<lastName>lastname1</lastName>
<department>IT</department>
<emailAddress>lastname1#mydomain.com</emailAddress>
<lineManagerId>22331470</lineManagerId>
<telephone_number>
<number1>0000000000</number1>
<number2>1111111111</number2>
<number3>2222222222</number3>
<retries1>2</retries1>
<retries2>1</retries2>
<retries3>2</retries3>
<numberType1>Mobile</numberType1>
<numberType2>Fixnet</numberType2>
<numberType3>Fixnet</numberType3>
</telephone_number>
</contact>
<contact>
<id>2</id>
<firstName>firstname2</firstName>
<lastName>lastname2</lastName>
<department>SUPPORT</department>
<emailAddress>lastname2#mydomain.com</emailAddress>
<lineManagerId>22331470</lineManagerId>
<telephone_number>
<number1>3333333333</number1>
<number2>4444444444</number2>
<number3>5555555555</number3>
<retries1>2</retries1>
<retries2>1</retries2>
<retries3>2</retries3>
<numberType1>Mobile</numberType1>
<numberType2>Fixnet</numberType2>
<numberType3>Fixnet</numberType3>
</telephone_number>
</contact>
</schema_name>
</e_schema>
now with this piece of code I read each of the contact node and add them to a list
var xmlcontacts = xmlloaded.Descendants("schema_name").Where(node => (string)node.Attribute("value") == comboSchema.SelectedValue.ToString());
foreach (XElement subelement in xmlcontacts.Descendants("contact")) //element is variable
{
contact.Add(new Contact()
{
id = subelement.Element("id").Value,
firstName = subelement.Element("firstName").Value,
lastName = subelement.Element("lastName").Value,
department = subelement.Element("department").Value,
emailAddress = subelement.Element("emailAddress").Value,
lineManagerId = subelement.Element("lineManagerId").Value,
//_phonenumbers = phones
});
}
but I do not have any Idea how I can read the node with the telephone_number
can someone give a hint or a line of code how I can do that!
I assume that _phonenumbers is some kind of collection, e.g. an IEnumerable<PhoneInfo>:
_phoneNumbers = subelement.Element("telephone_number").Elements()
.Where(e => e.Name.LocalName.StartsWith("number").Select(e =>
new PhoneInfo
{
Number = e.Value,
Retries = subelement.Element("telephone_Number").Element(
"retries" + e.Name.LocalName.SubString(5)).Value,
NumberType = subelement.Element("telephone_Number").Element(
"numbertype" + e.Name.LocalName.SubString(5)).Value
})
The code uses a linq expression to create a PhoneInfo instance for each number, and it looks up the corresponding retries and number type.
As a note: The xml structure is quite bad, it would be much better to have all the numbers in <number> tags with the actual number being the content and type retries and type data being attributes of that node.
_phonenumbers = subelement.Descendants("telephone_number")
.Select(x =>
new List<string>() {
(string)x.Element("number1"),
(string)x.Element("number2"),
(string)x.Element("number3")
});
I think you need to loop thru "telephone_number" element inside "contact" element.
Below is the code you can try:
var xmlcontacts = xmlloaded.Descendants("schema_name").Where(node => (string)node.Attribute("value") == comboSchema.SelectedValue.ToString());
foreach (XElement subelement in xmlcontacts.Descendants("contact")) //element is variable
{
contact.Add(new Contact()
{
id = subelement.Element("id").Value,
firstName = subelement.Element("firstName").Value,
lastName = subelement.Element("lastName").Value,
department = subelement.Element("department").Value,
emailAddress = subelement.Element("emailAddress").Value,
lineManagerId = subelement.Element("lineManagerId").Value,
//_phonenumbers = phones
});
foreach (XElement phoneElement in subelement.Descendants("telephone_number"))
{
//add telephone_number details in list here
}
}
I have just added one more foreach inside the "contact" loop

Select statement for xdoc query

I am trying to add a sub category to Messages in my xml statement Is there a way I can do this GroupMessages -> Message -> GroupMessage :
var groups = xDoc.Descendants("Group")
.Select(n => new
{
GroupName = n.Element("GroupName").Value,
GroupHeader = n.Element("GroupHeader").Value,
TimeCreated = DateTime.Parse(n.Element("TimeAdded").Value),
Tags = n.Element("Tags").Value,
Messages = n.Element("GroupMessages").Value
//line above
})
.ToList();
dataGrid2.ItemsSource = groups;
In my method GroupMessages contains both MessageID and GroupMessage and it is listing both in my datagrid within the one container. So I tried this but it lists nothing:
Messages = n.Descendants("GroupMessages").Select(nd => nd.Element("GroupMessage").Value)
My XML looks like this:
<Group>
<TimeAdded>2012-04-27T10:23:50.7153613+01:00</TimeAdded>
<GroupName>Group</GroupName>
<GroupHeader>Header</GroupHeader>
<GroupMessages>
<Message>
<MessageID>1</MessageID>
<GroupMessage>Message</GroupMessage>
<MessageGroup/>
</Message>
</GroupMessages>
</Group>
I have also tried:
Messages = n.Descendants("GroupMessages").Select(nd => nd.Descendants("Message").Select(nde => nde.Element("GroupMessage").Value))
To no avail?
Update:
private void ListGroups_Click(object sender, RoutedEventArgs e)
{
string uriGroup = "http://localhost:8000/Service/Group";
XDocument xDoc = XDocument.Load(uriGroup);
var groups = xDoc.Descendants("Group")
.Select(n => new
{
GroupName = n.Element("GroupName").Value,
GroupHeader = n.Element("GroupHeader").Value,
TimeCreated = n.Element("TimeAdded").Value,
Tags = n.Element("Tags").Value,
Messages = n.Element("GroupMessages").Descendants("Message").Select(nd => new
{
//Id = nd.Element("MessageID").Value,
Message = nd.Element("GroupMessage").Value
}).FirstOrDefault()
})
.ToList();
dataGrid2.ItemsSource = groups;
}
Unfortunatley this method shows "Collection" inside the cell in the datagrid. If I try ToArray it will show an array message inside the cell. Is there a way to actually display the GroupMessage? Not sure how you set the child elements of a datagrid?
At the most basic level, you can do this to get a single message (the first one):
var groups = from grp in xDoc.Descendants("Group")
select new {
GroupName = grp.Element("GroupName").Value,
GroupHeader = grp.Element("GroupHeader").Value,
TimeCreated = DateTime.Parse(grp.Element("TimeAdded").Value),
Message = grp.Element("GroupMessages").Element("Message").Element("GroupMessage").Value
};
However, I assume that you want Messages to be a list of messages with both ID and Message. In that case, consider this:
var groups = from grp in xDoc.Descendants("Group")
select new {
GroupName = grp.Element("GroupName").Value,
GroupHeader = grp.Element("GroupHeader").Value,
TimeCreated = DateTime.Parse(grp.Element("TimeAdded").Value),
Messages = grp.Element("GroupMessages")
.Descendants("Message")
.Select(msg => new {
Id = msg.Element("MessageID").Value,
Message = msg.Element("GroupMessage").Value
}).ToList()
};
However, I strongly stress that all this usage of anonymous classes is just going to cause confusion. If you have a class for Group and Message then use those.
Note that the problem you're having is you're ignoring the XML structure and selecting random elements. To get the value out of a single element, you're going to need to select exactly that element, and ask for .Value. Selecting it's parent, or it's parent's parent (as you did) is not enough.
Try this:
var groups = xDoc.Elements("Group")
.Select(n => new
{
GroupName = n.Get("GroupName", string.Empty),
GroupHeader = n.Get("GroupHeader", string.Empty),
TimeCreated = n.Get("TimeAdded", DateTime.MinValue),
Tags = n.Get("Tags", string.Empty),
Messages = n.GetEnumerable("GroupMessages/Message", m => new
{
Id = m.Get("MessageID", 0),
Message = m.Get("GroupMessage", string.Empty),
Group = m.Get("MessageGroup", string.Empty)
}).ToArray()
})
.ToList();
I used the extension methods from here: http://searisen.com/xmllib/extensions.wiki
Get will handle null cases like your Tags node that doesn't exist in your xml, as well as the empty tag MessageGroup. You should use Elements() if the node you want is the child of the node you are referencing and not any descendant by that name.
I copied your xml into a root node to test it. It works on this xml:
<root>
<Group>
<TimeAdded>2012-04-27T10:23:50.7153613+01:00</TimeAdded>
<GroupName>Group</GroupName>
<GroupHeader>Header</GroupHeader>
<GroupMessages>
<Message>
<MessageID>1</MessageID>
<GroupMessage>Message</GroupMessage>
<MessageGroup/>
</Message>
</GroupMessages>
</Group>
<Group>
<TimeAdded>2012-04-27T10:23:50.7153613+01:00</TimeAdded>
<GroupName>Group</GroupName>
<GroupHeader>Header</GroupHeader>
<GroupMessages>
<Message>
<MessageID>1</MessageID>
<GroupMessage>Message</GroupMessage>
<MessageGroup/>
</Message>
</GroupMessages>
</Group>
</root>

Categories

Resources