这一篇我总结一下,如何利用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文档这一方面可以说是很好,很强大。有兴趣的朋友可以参考有关的学习资料