Select statement for xdoc query

2019-07-15 06:17发布

问题:

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?

回答1:

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.



回答2:

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>