如何快速上手LINQ to XML

  在我们的程序中,我们经常需要将一些系统的数据、信息保存在文件中,而不是保存在数据库中,在.NET中,我通常都是选择将这些系统的数据、信息保存在XML中。

  操作XML的技术有很多种:

  1)DOM(Document Object Model,文档对象模型),它为XML文档提供了一个标准的解析。

  2)XPath和XSLT,它们提供了查询和格式化XML的功能。

  3).NET框架中提供了一些对XML操作的类(在System.XML命名空间下)。

  4)LINQ to XML。

  在我看来有了LINQ to XML技术,.NET中其它操纵XML的技术都可以弃而不用了,因为LINQ to XML操纵XML比其它技术都更简单更方便也更直观。

  LINQ to XML 是基于LINQ的,所以可以使用LINQ的所有功能,如标准查询操作符(详细可阅读《LINQ标准查询操作符详解》)和LINQ的编程接口。使用LINQ to XML可以很方便地将XML文件加载到内存中,对XML文档中的节点进行查询修改删除等各种操作,然后又可以很方便地将操作后的XML文档保存回磁盘。

  System.Xml.Linq的命名空间中包含了LINQ to XML处理XML用到的所有类,共有19个类,如下所示。

说明
公共类 Extensions 包含 LINQ to XML 扩展方法。
公共类 XAttribute 表示一个 XML 特性。
公共类 XCData 表示一个包含 CDATA 的文本节点。
公共类 XComment 表示一个 XML 注释。
公共类 XContainer 表示可包含其他节点的节点。
公共类 XDeclaration 表示一个 XML 声明。
公共类 XDocument 表示 XML 文档。
公共类 XDocumentType 表示 XML 文档类型定义 (DTD)。
公共类 XElement 表示一个 XML 元素。
公共类 XName 表示 XML 元素或特性的名称。
公共类 XNamespace 表示一个 XML 命名空间。 此类不能被继承。
公共类 XNode 表示 XML 树中节点的抽象概念(元素、注释、文档类型、处理指令或文本节点)。
公共类 XNodeDocumentOrderComparer 包含用于比较节点的文档顺序的功能。 无法继承此类。
公共类 XNodeEqualityComparer 比较节点以确定其是否相等。 无法继承此类。
公共类 XObject 表示 XML 树中的节点或特性。
公共类 XObjectChangeEventArgs 提供有关 ChangingChanged 事件的数据。
公共类 XProcessingInstruction 表示 XML 处理指令。
公共类 XStreamingElement 表示支持延迟流输出的 XML 树中的元素。
公共类 XText 表示一个文本节点。

  这19个类提供了很多很多的方法,事实上很少人会在学习LINQ to XML的时候去学习每一种的方法的细节,本文的目的是让从来没有使用过LINQ to XML的童鞋在需要使用LINQ to XML技术的时候快速上手,然后用之于自己的程序开发中,所以本文只讲LINQ to XML处理XML类中最常用、用到最多的三个类,分别是XDocument、XElement和XAttribute

  XDocument类派生于XContainer类,因此它可以有子节点,但XML的标准限制了XDocument对象只包含单个XElement子节点,因为XML文档只允许有一个根节点。

  XDocument提供了处理有效XML文档,包括声明、注释和处理指令。

  XDocument 可以包含以下元素:
     1)一个 XDeclaration 对象。 XDeclaration 使您能够指定 XML 声明的相关部分:XML 版本、文档的编码以及 XML 文档是否是独立的。
     2)一个 XElement 对象。 这是 XML 文档的根节点。
     3)任意数目的 XProcessingInstruction 对象。 处理指令将信息传递给处理 XML 的应用程序。
     4)任意数目的 XComment 对象。 注释将与根元素同级。 XComment 对象不能是列表中的第一个参数,因为 XML 文档以注释开头无效。
     5)一个用于 DTD 的 XDocumentType。

  XDocument类提供的方法可参考MSDN文档

 

  XElement派生自XContainer,而XContainer又派生于XNode类,所以一个元素也是一个节点。通过XElement可以创建XML的元素,添加和修改,移除元素以及子元素。

  XElement类提供了很多方法,因为一个XML文档中最为核心的东西就是XElement,这些方法使得我们处理XML提供如囊中取物般简单。

  XElement类提供的方法可以参考MSDN文档

 

  XAttribute派生于XObject类,不是派生于XNode类,所以XAttribute不能作为XML树中的节点,它是与XElement相关联的名称/值对,也就是XAttibute不能独立于元素而存在。

  XAttribute类提供的方法可参考MSDN文档

 

  本文的重点是讲述如何用LINQ to XML技术操纵XML文档,包含如何创建一个XML文档,如何保存XML文档,如何遍历XML文档元素,如何查找XML文档元素,如何更新XML文档的元素,如何删除XML文档元素等,下面我们假设一个应用场景来使用LINQ to XML技术来实现刚才所说的种种操作。

  场景:将中国的省市区信息保存在一个XML文档中,可以方便地对该文档进行各种操作,如查询,更新,删除元素等。

  下面我们先创建省市区的相关类,如下:

     

 public class Province
    {
       
/// <summary>
       
/// 省份名称
       
/// </summary>
       public string Name { getset; }
       
/// <summary>
       
/// 省会
       
/// </summary>
       public string Capital { getset; }

    }

    
public class City
    {
        
/// <summary>
        
/// 城市名称
        
/// </summary>
        public string Name { getset; }
        
/// <summary>
        
/// 城市编号
        
/// </summary>
        public string Code { getset; }

        
public Province Province { getset; }
    }

    
public class District
    {
        
/// <summary>
        
/// 区名
        
/// </summary>
        public string Name { getset; }

        
/// <summary>
        
/// 描述
        
/// </summary>
        public string Description { getset; }

        
public City City { getset; }
    }

 

 

      1)如何创建一个保存中国省市区信息的XML文档。

      使用LINQ to XML 创建一个XML文档非常简单,代码如下:

     

static void Main(string[] args)
        {
            
//获取当前应用程序目录下Area.xml文件的路径
            string _filePath = Path.Combine(System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "Area.xml");
            FileInfo fiXML 
= new FileInfo(_filePath);
            
//如果文件不存在
            if (!(fiXML.Exists))
            {
                XDocument xelLog 
= new XDocument(
                    
new XDeclaration("1.0""utf-8""yes"),
                    
new XComment("XML File For AREA"),
                    
new XElement("Provinces",
                                 
new XElement("Province"new XAttribute("Name""省份"),
                                              
new XElement("City"new XAttribute("Name""城市"),
                                                           
new XElement("District"new XAttribute("Name""行政区")))

                                     )
                        )
                    );
                xelLog.Save(_filePath);
            }
        }

 

  这段代码使用指定的内容初始化 XDocument 类的新实例,然后调用XDocument的Save方法来生成一个XML文档。很少使用XDocument来创建XML树,通常是使用 XElement 根节点创建 XML 树。除非具有创建文档的具体要求(例如,必须在顶级创建处理指令和注释,或者必须支持文档类型),否则使用 XElement 作为根节点通常会更方便。运行这段代码,就会在生成的应用程序根目录下创建一个名为Area.xml的文档,文档内容如下:

     

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!--XML File For AREA-->
<Provinces>
  
<Province Name="省份">
    
<City Name="城市">
      
<District Name="行政区" />
    
</City>
  
</Province>
</Provinces>

 

   2)如何将一个XML树加载到程序内存。

      我们操作一个XML文档首先是需要将该文档加载到程序的内存中,在LINQ to XML中,通常是使用XElement类型的Load方法将XML文档自根节点开始的XML树加载到一个XElement类型的对象中,然后我们就可以采用XElement提供的各种方法对这个内存中XML文档进行各种操作。

       我们创建一个LINQtoXML的帮助类LinqToXmlHelper.cs,将对XML操作的相关方法都写在这个类里面,下面是一个加载XML文档到XElement对象的方法。

      

 /// <summary>
        
/// 将Area.xml文档加载到内存中的XElement类型的对象xElement,成功调用 XElement.Load方法后会在xElement保存整棵XML树
        
/// </summary>
        
/// <returns></returns>
        public XElement Load()
        {
            XElement xElement 
= XElement.Load(_filePath);
            
return xElement;
        }

 

       我们刚才已经创建了一个XML文档,现在我们将这个文档加载到内存中,然后打印这个XElement对象。

      

static void Main(string[] args)
        {
           
 LinqToXmlHelper linqToXmlHelper = new LinqToXmlHelper();
            var elements 
= linqToXmlHelper.Load();
            
string str = elements.ToString();

            Console.WriteLine(str);
            Console.ReadKey();
}

 

      输出结果如下:

     

 

      3)如何向现有的XML文档插入新的元素

  到现在,我们已经了解了LINQ to XML中创建和加载XML文档的方式,接下来的问题就是如何向一个已经存在的XML文档添加新的节点。下面是增加新元素的方法,我们可以向Area.xml文档中添加新是省份城市和区域信息,如果已经存在的区域则进行更新,这里用XElement的Save对更改后的XML文档进行保存。

     

 /// <summary>
        
/// 增加新元素,如果要增加的区已经存在,则对区的Description进行更新
        
/// </summary>
        
/// <param name="districts"></param>
        public void AddElement(IList<District> districts)
        {
            
if(districts==null||districts.Count==0return;

            XElement xElement 
= Load();
            
foreach (District district in districts)
            {
                
if (district.City == null || district.City.Province == null)
                {
                    
continue;
                }
                XElement provinceElement 
= xElement.Elements("Province").Where(e => ((string)e.Attribute("Name")).Equals(district.City.Province.Name)).FirstOrDefault();
               
//判断该省份是否存在,不存在曾增加该省份的节点
                if(provinceElement==null)
                {
                     provinceElement 
= new XElement("Province"new XAttribute("Name", district.City.Province.Name), new XAttribute("Capital", district.City.Province.Capital));
                    xElement.Add(provinceElement);
                }

                XElement cityElement 
= provinceElement.Elements("City").Where(e => ((string)e.Attribute("Name")).Equals(district.City.Name)).FirstOrDefault();
                
//判断该城市是否存在,不存在则在对应省份下增加该城市的节点
                if(cityElement==null)
                {
                    cityElement 
= new XElement("City"new XAttribute("Name", district.City.Name), new XAttribute("Code", district.City.Code));
                    provinceElement.Add(cityElement);
                }

                XElement districtElement 
= xElement.Elements("District").Where(e => ((string)e.Attribute("Name")).Equals(district.Name)).FirstOrDefault();
                
//如果存在该区域的节点则先删除在添加,以这种方式更新节点
                if(districtElement!=null)
                {
                    districtElement.Remove();
                }
                districtElement 
= new XElement("District"new XAttribute("Name", district.Name), new XAttribute("Description", district.Description));
                cityElement.Add(districtElement);

            }
           
//操作完毕,将内存中的XML树保存回硬盘的XML文档中。
          xElement.Save(_filePath);
        }

 

      上面的AddElement方法中,调用XElement对象的传一个XElement对象的Add方法,可以将一个XElement对象(节点)添加为一个已有节点的最后子节点。这样我们通过Add的方式就可以轻易扩展一个节点的子节点。

  

      下面调用增加新元素AddElement方法,将一个区的集合信息保存到Area.xml文档中,然后打印XML树。代码如下:

      

 static void Main(string[] args)
        {
            
            LinqToXmlHelper linqToXmlHelper 
= new LinqToXmlHelper();
            Province province 
= new Province() { Name = "广东省", Capital = "广州市" };
            City city 
= new City() { Name = "广州市", Code = "020", Province = province };
            IList
<District> districts = new List<District>()
                                            { 
new District() {City = city, Name = "天河区", Description = "天河区的描述"}, new District() {City = city, Name = "越秀区", Description = "越秀区的描述"}
                                                
                                            };
            linqToXmlHelper.AddElement(districts);
            XElement xElement 
= linqToXmlHelper.Load();
            Console.WriteLine(xElement.ToString());
}

 

      输出结果如图:

     

 

      4)如何查询和遍历XML文档的元素

  根据XElement类型提供的Elements方法,我们可以获取某个节点的所有子节点元素,也可以通过传入节点名称为参数,获取某个节点下所有和参数节点名称相匹配的节点集合,可以根据XElement类型提供的Attibute方法获取某节点的属性信息,下面代码示例根据城市名查询该城市下所有的区的信息:

     

 /// <summary>
        
/// 根据城市名查询该城市下所有的区的信息
        
/// </summary>
        
/// <param name="cityName"></param>
        
/// <returns></returns>
        public IList<District> GetDistricts(string cityName)
        {
            IList
<District> districts = new List<District>();
            XElement xElement 
= Load();
            
//获取城市名为变量cityName的节点的所有子节点
            IEnumerable<XElement> xElements =
                xElement.Elements(
"Province").Elements().Where(e => ((string) e.Attribute("Name")).Equals(cityName)).Elements();
            
foreach (XElement element in xElements)
            {
                District district 
= new District();
                district.Name 
= (string) element.Attribute("Name");
                district.Description 
= (string) element.Attribute("Description");
                districts.Add(district);

            }
            
return districts;
        }

 

  调用GetDistricts方法

     

 static void Main(string[] args)
        {
            
            LinqToXmlHelper linqToXmlHelper 
= new LinqToXmlHelper();
            IList
<District> districts = linqToXmlHelper.GetDistricts("广州市");
            
foreach (var district in districts)
            {
                Console.WriteLine(district.Name);
            }
          }

 

  输出结果为:

  天河区

  越秀区

  

      5)如何删除XML文档中的节点元素

  删除区的节点元素的方法,这里假定区的名称是唯一的

     

 /// <summary>
        
/// 删除区的节点元素,这里假定区的名称是唯一的
        
/// </summary>
        
/// <param name="districts"></param>
        public void DeleteElement(IList<District> districts)
        {
            XElement xElement 
= Load();
            
foreach (District district in districts)
            {
               
                XElement districtElement 
= xElement.Elements("Province").Elements("City").Elements("District").Where(e => ((string)e.Attribute("Name")).Equals(district.Name)).FirstOrDefault();
                
if(districtElement!=null)
                {
                    districtElement.Remove();
                }
            }
            xElement.Save(_filePath);

        }

 

  调用XElement.Remove方法可以在当前节点的父节点上删除当前节点。下面代码删除区节点中名称为“越秀区”的节点。

     

static void Main(string[] args)
        {
            
            LinqToXmlHelper linqToXmlHelper 
= new LinqToXmlHelper();
            IList
<District> districts = new List<District>(){new District(){Name = "越秀区"} };
            linqToXmlHelper.DeleteElement(districts);
            XElement xElement 
= linqToXmlHelper.Load();
            Console.WriteLine(xElement.ToString());
            Console.ReadKey();

        }

 

  输出结果为:

     

  

 

     到这里,你已经看到了LINQ to XML技术操作XML最为常用一些功能,包括创建和加载XML文档,新增删除更新XML节点等。如果你未能更好理解本文,或者本文组织的不够好,那么请下载本文的代码参考。代码下载

  

 

     参考文档:

  微软 MSDN

  Scott Klein Professional LINQ (LINQ高级编程)

posted on 2011-07-25 10:53  边写边唱  阅读(2558)  评论(5编辑  收藏  举报

导航