如何使用LINQ to XML读取此XML

本文关键字:XML 读取 to 何使用 LINQ | 更新日期: 2023-09-27 18:23:54

我是LINQ to XML的新手,目前正在使用以下XML:

<invoices>
  <invoice>
    <order_id>85</order_id>
    <time>02:52 PM</time>
    <date>24-05-2013</date>
    <order>
      <item>
        <Main>
          <id>343</id>
          <Qty>1</Qty>
        </Main>
        <Add />
      </item>
      <item>
        <Main>
          <id>3</id>
          <Qty>1</Qty>
        </Main>
        <Add>
          <Extra id="1">
            <Qty>1</Qty>
            <Desc>Regular</Desc>
          </Extra>
        </Add>
      </item>
    </order>
  </invoice>
  <invoice>
    <order_id>88</order_id>
    <time>03:10 PM</time>
    <date>24-05-2013</date>
    <order>
      <item>
        <Main>
          <id>345</id>
          <Qty>1</Qty>
        </Main>
        <Add />
      </item>
      <item>
        <Main>
          <id>2</id>
          <Qty>2</Qty>
        </Main>
        <Add>
          <Extra id="1">
            <Qty>1</Qty>
            <Desc>Regular</Desc>
          </Extra>
        </Add>
      </item>
    </order>
  </invoice>
</invoices>

到目前为止,我已经编写了以下代码:

void queryData(XDocument doc)
{
        var data = from item in doc.Descendants("invoice")
                   select new
                   {
                       orderId = item.Element("order_id").Value,
                       orderDate = item.Element("date").Value,
                       orderTime = item.Element("time").Value
                   };
        foreach(var p in data)
            Console.WriteLine(p.ToString());
        //...
}

我在读取"order"标记中的嵌套标记时遇到问题。此外,元素/标记"Add"有时有"Extra"数量的标记/元素,有时没有。

我无法访问生成该xml的代码,因此必须阅读此模式。

到目前为止,我已经尝试过分组,但无法处理2级和3级元素。

阅读后,我会将这些值保存到数据库中。

谢谢,

如何使用LINQ to XML读取此XML

如果我有什么问题,请告诉我,我没有机会测试它,还必须假设一些元素将被复制

var data = from item in doc.Descendants ( "invoice" )
    select new {
        orderId = item.Element ( "order_id" ).Value ,
        orderDate = item.Element ( "date" ).Value ,
        orderTime = item.Element ( "time" ).Value ,
        items = 
            from order in item.Element ( "order" ).Descendants ( "item" )
            let main = order.Element ( "Main" )
            let adds = order.Elements ( "Add" )
            select new {
                Main = new {
                    id = main.Element ( "id" ).Value ,
                    Qty = main.Element ( "Qty" ).Value
                } ,
                Add = 
                (from add in adds
                    let extras = add.Elements ( "Extra" )
                    select new {
                                Extra = ( from extra in extras
                                        select new {
                                                extraId = extra.Attribute("id").Value,
                                                Qty = extra.Element ( "Qty" ).Value ,
                                                Desc = extra.Element ( "Desc" ).Value
                                            }).FirstOrDefault ( )
                            }).FirstOrDefault()
            }
};

对于嵌套元素,只需继续使用.Element("name"):

orderQuantities = item.Element("order").Elements("item")
    .Select(orderItem => new { 
        id = orderItem.Element("Main").Element("id")),
        qty = orderItem.Element("Main").Element("Qty"))
     }).ToArray(),

对于您不确定是否存在的元素,您可以随时编写一个辅助方法:

extraQty = GetExtra(item),

其中GetExtra类似于:

public int GetExtra(XElement element)
{
    XElement extra = element.Element("Add").Element("Extra");
    if (extra != null) return int.Parse(extra.Element("Qty").Value);
    else return 0;
}

(当然需要更多的错误处理,但你已经明白了。)

以下是您的xml解析:

var parser = new Parser();
XDocument xdoc = XDocument.Load(path_to_xml);
var orders = from invoice in xdoc.Root.Elements()
             select parser.ParseOrderFrom(invoice);

仅此而已。我创建了以下类。订单,它包含订单项目的集合,并具有很好的解析日期:

public class Order
{
    public int Id { get; set; }
    public DateTime Date { get; set; }
    public List<OrderItem> Items { get; set; }
}

点菜项目,这是你的主菜。此外,里面还有额外的列表(如果有的话):

public class OrderItem
{
    public int Id { get; set; }
    public int Quantity { get; set; }
    public List<Extra> Extras { get; set; }
}

和附加类:

public class Extra
{
    public int Id { get; set; }
    public int Quantity { get; set; }
    public string Description { get; set; }
}

如果您愿意,所有解析都发生在单独的解析器类中(这将保持域类的干净):

public class Parser
{
    public Order ParseOrderFrom(XElement invoice)
    {
        string time = (string)invoice.Element("time");
        string date = (string)invoice.Element("date");
        return new Order {
           Id = (int)invoice.Element("order_id"),
           Date = DateTime.ParseExact(date + time, "dd-MM-yyyyhh:mm tt", null),
           Items = invoice.Element("order").Elements("item")
                          .Select(i => ParseOrderItemFrom(i)).ToList()
        };
    }
    public OrderItem ParseOrderItemFrom(XElement item)
    {
        var main = item.Element("Main");
        return new OrderItem {
            Id = (int)main.Element("id"),
            Quantity = (int)main.Element("Qty"),
            Extras = item.Element("Add").Elements("Extra")
                         .Select(e => ParseExtraFrom(e)).ToList()
        };
    }
    public Extra ParseExtraFrom(XElement extra)
    {
        return new Extra {
            Id = (int)extra.Attribute("id"),
            Quantity = (int)extra.Element("Qty"),
            Description = (string)extra.Element("Desc")
        };
    }
}

如果在查询中使用一些xpath,可以使事情更易于管理。如果你问我,在这里使用纯LINQ到XML可能会变得过于冗长

var query =
    from invoice in doc.XPathSelectElements("/invoices/invoice")
    select new
    {
        OrderId = (int)invoice.Element("order_id"),
        Time = (string)invoice.Element("time"),
        Date = (string)invoice.Element("date"),
        Items =
            from item in invoice.XPathSelectElements("./order/item")
            select new
            {
                Id = (int)item.XPathSelectElement("./Main/id"),
                Quantity = (int)item.XPathSelectElement("./Main/Qty"),
                Extras =
                    from extra in item.XPathSelectElements("./Add/Extra")
                    select new
                    {
                        Id = (int)extra.Attribute("id"),
                        Quantity = (int)extra.Element("Qty"),
                        Description = (string)extra.Element("Desc"),
                    },
            },
    };

测试了一个工作。如果不定义一些额外的类,这是不可能一次性完成的。这里我有一个枢轴接口Item,然后有两个实现接口AdditemMainItem的类。

请随意询问任何部分的解释。

// Since there are different types of items, we need an interface/abstact
// class to pivot.
public interface Item {
}
// The information neccesary for storing the 'Extra' element.
public class Extra {
    public Int32 ID { get; private set; }
    public Int32 Quantity { get; private set; }
    public String Description { get; private set; }
    public Extra(XElement extra) {
        // Here we load up all of the details from the 'extra' element
        this.ID = Int32.Parse(extra.Attribute("id").Value);
        this.Quantity = Int32.Parse(extra.Element("Qty").Value); ;
        this.Description = extra.Element("Desc").Value;
    }
}
// The 'add-item' is associated with the 'add' tag in the actual XML.
public class AddItem : Item {
    public IEnumerable<Extra> Extras { get; private set; }
    // The 'extras' is a collection of many items, so we require
    // an ienumerable.
    public AddItem(IEnumerable<Extra> extras) {
        this.Extras = extras;
    }
}
// The storage for the 'main-item'
public class MainItem : Item {
    public Int32 ID { get; private set; }
    public Int32 Quantity { get; private set; }
    public MainItem(Int32 id, Int32 quantity) {
        this.ID = id;
        this.Quantity = quantity;
    }
}
class Program {
    static void Main(string[] args) {
        String data = File.ReadAllText("File.txt");
        XElement tree = XElement.Parse(data);

        var projection = tree.Elements()
            .Select(invoice => new {
                // Project the main details of the invoice { OrderID, Time, Date, Order }
                // The order itself needs to be projected again though because it too is a 
                // collection of sub items.
                OrderID = invoice.Element("order_id").Value,
                Time = invoice.Element("time").Value,
                Date = invoice.Element("date").Value,
                Order = invoice.Element("order")
                    .Elements()
                    .Elements()
                    .Select(item => {
                        // First, we need to know what type of item this 'order' is.
                        String itemType = item.Name.ToString();
                        // If its a 'main' item, then return that type.
                        if (itemType == "Main") {
                            Int32 id = Int32.Parse(item.Element("id").Value);
                            Int32 quantity = Int32.Parse(item.Element("Qty").Value);
                            return (Item)new MainItem(id, quantity);
                        }
                        // If it's an 'Add' item. Then we have to:
                        if (itemType == "Add") {
                            // (1) Capture all of the extras.
                            IEnumerable<Extra> extras = item.Elements()
                                .Select(extra => new Extra(extra))
                                .ToList();
                            // (2) Add the extras to a new AddItem. Then return the 'add'-item.
                            // Notice that we have to cast to 'Item' because we are returning 
                            // a 'Main'-item sometimes and an 'add' item other times.
                            // Select requires the return type to be the same regardless.
                            return (Item)new AddItem(extras);
                        }
                        // Hopefully this path never hits.
                        throw new NotImplementedException("This path not defined");
                    }).ToList()
            }).ToList();
        Console.WriteLine(projection);
    }
}