使用XPATH对XML数据进行解析
这一篇我总结一下,如何利用XPATH及相关技术对XML数据文档进行查询和处理
1. 首先还是来看一下范例数据
<?xml version="1.0" encoding="utf-8" ?> <Orders><!--所有订单--> <Order OrderID="1" OrderDate="2008-12-17"><!--一个订单--> <OrderItems><!--订单的明细--> <Item> <ProductID>1</ProductID> <Quantity>2.0</Quantity> <UnitPrice>25.5</UnitPrice> </Item> <Item> <ProductID>2</ProductID> <Quantity>2.0</Quantity> <UnitPrice>5.5</UnitPrice> </Item> <Item> <ProductID>3</ProductID> <Quantity>29.0</Quantity> <UnitPrice>300.5</UnitPrice> </Item> </OrderItems> </Order> <Order OrderID="2" OrderDate="2009-01-01"> <OrderItems> <Item> <ProductID>1</ProductID> <Quantity>2.0</Quantity> <UnitPrice>25.5</UnitPrice> </Item> </OrderItems> </Order> </Orders>
这是一个典型的订单数据。Orders下面可以有一个或者多个Order,每个Order有两个属性:OrderID,OrderDate,同时,每个Order都有一个或者多个OrderItem
2. 这个数据文件的架构如下
<?xml version="1.0" encoding="utf-8"?> <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="Orders"> <xs:complexType> <xs:sequence> <xs:element maxOccurs="unbounded" name="Order"> <xs:complexType> <xs:sequence> <xs:element name="OrderItems"> <xs:complexType> <xs:sequence> <xs:element maxOccurs="unbounded" name="Item" minOccurs="1"> <xs:complexType> <xs:sequence> <xs:element name="ProductID" type="xs:unsignedByte" /> <xs:element name="Quantity" type="xs:decimal" /> <xs:element name="UnitPrice" type="xs:decimal" /> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="OrderID" type="xs:unsignedByte" use="required" /> <xs:attribute name="OrderDate" type="xs:date" use="required" /> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
3. 下面来看看,我们如何查询某个订单
需求1:查询OrderID=1的订单,显示出来它的所有OrderItem
using System; using System.Xml; namespace XPathSample { class Program { static void Main(string[] args) { string dataFile = "../../Order.XML"; QueryByOrderID(dataFile, "1"); Console.Read(); } static void QueryByOrderID(string file, string orderID) { XmlDocument doc = new XmlDocument(); doc.Load(file); XmlNode node = doc.SelectSingleNode("/Orders/Order[@OrderID=" + orderID + "]"); if (node != null) { Console.WriteLine("订单编号为:{0}", node.Attributes["OrderID"].Value); Console.WriteLine("订单日期为:{0}", node.Attributes["OrderDate"].Value); XmlNodeList items = node.SelectNodes("OrderItems/Item"); if (items != null) { Console.WriteLine("订单明细为:{0}", items.Count); foreach (XmlNode n in items) { Console.WriteLine("产品:{0},单价:{1},数量:{2}", n.SelectSingleNode("ProductID").InnerText, n.SelectSingleNode("UnitPrice").InnerText, n.SelectSingleNode("Quantity").InnerText ); } } } } } }
关于更多语法,请参考 http://www.w3school.com.cn/xpath/xpath_syntax.asp
需求2:查询所有单价大于20的订单明细记录
static void QueryByUnitPrice(string file) { //查询所有单价大于20的订单明细记录 XmlDocument doc = new XmlDocument(); doc.Load(file); XmlNodeList list = doc.SelectNodes("//Item[UnitPrice>20]"); if (list != null) { foreach (XmlNode node in list) { Console.WriteLine("订单编号:{0},订购日期:{1},产品编号:{2},单价{3},数量:{4}", node.ParentNode.ParentNode.Attributes["OrderID"].Value, node.ParentNode.ParentNode.Attributes["OrderDate"].Value, node.SelectSingleNode("ProductID").InnerText, node.SelectSingleNode("UnitPrice").InnerText, node.SelectSingleNode("Quantity").InnerText ); } } }
需求3:多个条件查询。我们查询单价大于20并且数量也大于20的订单明细。
XmlNodeList list = doc.SelectNodes("//Item[UnitPrice>20 and Quantity>20]");
有关其他更多的运算符,请参考http://www.w3school.com.cn/xpath/xpath_operators.asp
4. 如何处理命名空间的问题。
假设,我们的数据文件含有命名空间,那么在查询的时候应该如何处理呢?
<?xml version="1.0" encoding="utf-8" ?> <Orders xmlns:d="http://www.xizhang.com"><!--所有订单--> <d:Order OrderID="1" OrderDate="2008-12-17"><!--一个订单--> <OrderItems><!--订单的明细--> <Item> <ProductID>1</ProductID> <Quantity>2.0</Quantity> <UnitPrice>25.5</UnitPrice> </Item> <Item> <ProductID>2</ProductID> <Quantity>2.0</Quantity> <UnitPrice>5.5</UnitPrice> </Item> <Item> <ProductID>3</ProductID> <Quantity>29.0</Quantity> <UnitPrice>300.5</UnitPrice> </Item> </OrderItems> </d:Order> <Order OrderID="2" OrderDate="2009-01-01"> <OrderItems> <Item> <ProductID>1</ProductID> <Quantity>2.0</Quantity> <UnitPrice>25.5</UnitPrice> </Item> </OrderItems> </Order> </Orders>
我们添加了一个命名空间,请注意,此时第一个订单(带d前缀的)和第二个订单(不带d前缀的)就不同了。
如果我们要查询第一个订单的所有Item,那么该如何写代码呢?
static void QueryWithNamespace(string file) { XmlDocument doc = new XmlDocument(); doc.Load(file); XmlNamespaceManager xnm = new XmlNamespaceManager(doc.NameTable); xnm.AddNamespace("d", "http://www.xizhang.com"); XmlNode order = doc.SelectSingleNode("/Orders/d:Order[@OrderID=1]",xnm); Console.WriteLine("订单编号:{0}", order.Attributes["OrderID"].Value); Console.WriteLine("订单日期:{0}", order.Attributes["OrderDate"].Value); XmlNodeList items = order.SelectNodes("OrderItems/Item"); if (items != null) { Console.WriteLine("订单明细为:{0}", items.Count); foreach (XmlNode n in items) { Console.WriteLine("产品:{0},单价:{1},数量:{2}", n.SelectSingleNode("ProductID").InnerText, n.SelectSingleNode("UnitPrice").InnerText, n.SelectSingleNode("Quantity").InnerText ); } } }
5. 如何使用LINQ TO XML查询
LINQ TO XML是.NET 3.5中的一个重大增强,极大地方便了我们编写对XML文档的查询
要使用该功能,必须引用两个程序集(如果你当前的项目并没有使用最新的.NET Framework 3.5的话)
这两个程序集在如下的目录:C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5
出现这个警告,请点击 “是”
但是,仍然有个警告。要想去除这个警告,请选中该程序集,按F4
将“特定版本”设置为false。
这样就可以了。
然后,我们需要添加两个using语句,导入两个命名空间
using System.Linq; using System.Xml.Linq;
编写查询的方法
static void QueryWithNamespaceByLinq(string file) { XDocument doc = XDocument.Load(file); XNamespace xnamespace = "http://www.xizhang.com"; var query = from order in doc.Element("Orders").Elements(xnamespace + "Order") select order; foreach (var item in query) { Console.WriteLine("订单编号:{0}", item.Attribute("OrderID").Value); Console.WriteLine("订单日期:{0}", item.Attribute("OrderDate").Value); var items = from orderitem in item.Descendants("Item") select orderitem; foreach (var subitem in items) { Console.WriteLine("产品:{0},单价:{1},数量:{2}", subitem.Element("ProductID").Value, subitem.Element("UnitPrice").Value, subitem.Element("Quantity").Value ); } } }
大家可能会说,这样看起来LINQ TO XML的语法并没有太多优势,因为几乎差不多。但其实LINQ TO XML还是有很多很好的特性的,例如排序,聚合,分组,联接等等,而且在构造XML文档这一方面可以说是很好,很强大。有兴趣的朋友可以参考有关的学习资料