Aaron McKelvie
Aaron McKelvie

Reputation: 167

How to get multiple XML elements that are descendants of another element

I have an XML document that can have multiple children elements under another element, and I was wondering how I can get all these elements and store as an object? For example-

<?xml version="1.0" encoding="utf-8" ?>
<export>
  <order>
    <ordernumber>100</ordernumber>
    <items>
      <item>
        <name>table</name>
      </item>
      <item>
        <name>chair</name>
      </item>
    </items>
  </order>
</export>

So say I get all orders using LINQ and store in a list

var xdoc = XDocument.Load(myXMLReader);

var result = (from x in xdoc.Root.Elements()
              select new Order
              {
                  OrderNumber = (string)x.Element("OrderNumber")
              }).ToList();

What do I need to do to the above to get ALL items in an order and store in another object property such as a List or something similar? EG.

var result = (from x in xdoc.Root.Elements()
              select new Order
              {
                  OrderNumber = (string)x.Element("OrderNumber")

                  //PSUEDO CODE ADDITION
                  Items = (new { Name = itemname}).ToList()
                  // END PSUEDO CODE ADDITION

              }).ToList();

Upvotes: 1

Views: 3088

Answers (3)

John Castleman
John Castleman

Reputation: 1561

Based on the root element being named export, I thought you might be trying to serialize/de-serialize objects in your program:

[XmlRoot("export")]
public class Export
{
    [XmlElement("order")]
    public Order order {get; set;}
}

public class Order
{
    [XmlElement("ordernumber")]
    public int orderNumber { get; set; }
    [XmlArray("items"), XmlArrayItem("item")]
    public Item[] items { get; set; }
}

public class Item
{
    public string name { get; set; }
}

static void Serialize(string file, Export export)
{
    var serializer = new XmlSerializer(typeof(Export));
    using (var stream = File.Create(file))
        serializer.Serialize(stream, export);
}

static Export Deserialize(string file)
{
    var serializer = new XmlSerializer(typeof(Export));
    using (var stream = File.OpenRead(file))
        return (Export) serializer.Deserialize(stream);
}

You would call it like this:

var export = new Export
{
    order = new Order
    {
        orderNumber = 100,
        items = new[]
        {
            new Item {name = "table"},
            new Item {name = "chair"}
        }
    }
};

Serialize("exported_orders.xml", export);

and like this:

var export = Deserialize("exported_orders.xml");

Upvotes: 0

Rahul Singh
Rahul Singh

Reputation: 21825

I think you need this:-

List<Order> orders = xdoc.Descendants("order")
                    .Select(x => new Order
                    {
                      OrderNumber = (string)x.Element("ordernumber"),
                      Items = x.Descendants("item")
                               .Select(i => new Item
                               {
                                  Name = (string)i.Element("name") }).ToList()
                               }).ToList();

Type I have used:-

public class Order
{
    public string OrderNumber { get; set; }
    public List<Item> Items { get; set; }
}

public class Item
{
    public string Name { get; set; }
}

Upvotes: 2

Peter Duniho
Peter Duniho

Reputation: 70701

I'm not entirely sure what you're trying to do. But I think it might be something like this:

var result = (from x in xdoc.Root.Elements()
              select new Order
              {
                  OrderNumber = (string)x.Element("ordernumber"),
                  Items = x.Element("items")
                           .Elements("item")
                           .Select(itemElement =>
                               new Item { Name = itemElement.Value })
                           .ToList()
              }).ToList();

In other words: for the given element, first find the one items child element, then from that element, select all of its item child elements, and finally from that collection, project to a collection of Item objects, to be materialized as a List<Item> and assigned to the Order.Items property.

(Note: you were mixing the named type Order with the anonymous type representing an item XML element; I fixed the code so that it uses named types everywhere, but you can of course use anonymous everywhere instead if you like).

Upvotes: 2

Related Questions