如何使用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级元素。
阅读后,我会将这些值保存到数据库中。
谢谢,
如果我有什么问题,请告诉我,我没有机会测试它,还必须假设一些元素将被复制
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
,然后有两个实现接口Additem
和MainItem
的类。
请随意询问任何部分的解释。
// 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);
}
}