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>
Related
I have the following XML
<Configuration>
<Organisation Count="2">
<ID>1234</ID>
<UKPRN Count = "2">
<NAME>
<FIRST>abcd</FIRST>
<LAST>efgh</LAST>
</NAME>
</UKPRN>
</Organisation>
</Configuration>
I've tried this
var test = root.Elements().Where(p => p.Attribute("Count") != null).Select(p => p.Descendants("FIRST"));
but that's the wrong way round. Can't quite get my head around how to get hold of the parent.
How do I use linq to identify the closest parent element with the attribute Count, e.g. element FIRST should return UKPRN and element ID should return Organisation?
You can use the Ancestors() method to get all the parents and choose the first one that has the attribute you require:
var pairs = root.Descendants()
.Select(e => new
{
Element = e,
CountElement = e.Ancestors().FirstOrDefault(a => a.Attribute("Count") != null)
});
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.
I am trying to parse the xml below to load the id_name/rel_no pairs into an anonymous type collection. I am having a problem when looping through the collection and when element is missing in one of the elements. Is there a way not to load a particular pair when one of the elements id_name or rel_no is missing?
I get InvalidOperationException (sequence contains no elements) when the loop gets to that particular pair with missing element.
Thanks for any suggestions.
XDocument xdata = XDocument.Parse(data);
var query = from dox in xdata.Descendants("Inc")
select new
{
IDName= dox.Element("id_name").Value,
RelNo= dox.Descendants("rel_no").First().Value
};
XML
<Data>
<Inc>
<id_name>test</id_name>
<Relationships>
<Relationship>
<rel_no>004</rel_no>
</Relationship>
</Relationships>
</Inc>
<Inc>
<id_name>test2</id_name>
<Relationships>
<Relationship>
</Relationship>
</Relationships>
</Inc>
<Inc>
<id_name>test3</id_name>
<Relationships>
<Relationship>
<rel_no>006</rel_no>
</Relationship>
</Relationships>
</Inc>
</Data>
Accessing in a loop
foreach (var record in query)
{
}
var xdata = XDocument.Parse(data);
var items = xdata.Descendants("Inc")
.Select(d => new
{
DName = (string)d.Element("id_name"),
RelNo = ((string)d.Descendants("rel_no").FirstOrDefault() ?? "")
})
.ToList();
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
<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();