当前位置: 首页 > news >正文

C# LINQ(LINQ to XML)

LINQ to XML

可扩展标记语言(XML)是存储和交换数据的重要方法。LINQ为该语言增加了一些特性,
使得XML用起来比XPath和XSLT等方法容易得多。如果你熟悉这些方法的话,会很高兴LINQ
to XML在许多方面简化了XML的创建、查询和操作。

  • 可以使用单一语句自顶向下创建XML树。
  • 可以在不使用包含树的XML文档的情况下在内存中创建并操作XML。
  • 可以在不使用Text子节点的情况下创建和操作字符串节点。
  • 一个最大的不同(改进)是,在搜索一个树时,不再需要遍历它。相反,只需要查
    询树并让它返回结果。

尽管本书不会完整介绍XML,但是在介绍LINQ提供的一些XML操作特性之前,会先简单
介绍一下XML。

标记语言

标记语言(markuplanguage)是文档中的一组标签,它提供有关文档的信息并组织其内容。
也就是说,标记标签不是文档的数据一一它们包含关于数据的数据。有关数据的数据称为元数据。

标记语言是定义的一组标签,旨在传递有关文档内容的特定类型的元数据。例如,HTML是
众所周知的标记语言。标签中的元数据包含了web页面如何在浏览器中呈现以及如何使用超链
接在页面中导航的信息。

大多数标记语言包含一组预定义的标签,而几只包含少量预定义的标签,其他都由程序
员来定义,用来表示特定文档类型需要的任何元数据。只要数据的读者和编写者都知道标签的含
义,标签就可以包含设计者想要的任何有用信息。

XML基础

XWL文档中的数据包含在一个XML树中,XML树主要由嵌套元素组成。
元素是树的基本要素。每一个元素都有名字并且包含数据,一些元素还可以包含其他
嵌套元素。元素由开始和关闭标签进行划分。元素包含的任何数据都必须介于开始和关闭标签
之间。

  • 开始标签从一个左尖括号开始,后面跟元素名,紧接着是可选的特性,最后是右尖括号。
  • 关闭标签从一个左尖括号开始,后面是斜杠,然后是元素名和右尖括号。
  • 没有内容的元素可以直接由单个标签表示,从左尖括号开始,后面是元素名和斜杠,最
    后以右尖括号结束。

image

如下片段演示了一个叫作EmployeeName的元素,后面是空元素PhoneNumber。
其他需要了解的有关L的重要事项如下。

  • XML 文档必须有一个根元素来包含所有其他元素。
  • XML标签必须合理嵌套。
  • 与HTML标签不同,XML标签是区分大小写的。
  • XNL特性是名/值对,它包含了元素的其他元数据。特性的值部分必须包含在引号内,可
    以是单引号也可以是双引号。
  • XML文档中的空格是有效的。这与把空格作为单个空格输出的HTNIL不同。
    下面的化文档是包含两个员工信息的XML示例。这个XML树非常简单,可以清晰显示
    元素。需要了解的有关这个潘IL树的重要事项如下。
  • 树包含了一个Emlopyees类型的根节点,它包含了两个Employee类型的子节点。
  • 每一个Employee节点包含了包含员工姓名和电话的节点。
<Employees> <Employee> <Name>Bob Smith</Name> <PhoneNumber>408-555-1000</PhoneNumber> <CellPhone /> </Employee> <Employee> <Name>Sally Jones</Name> <PhoneNumber>415-555-2000</PhoneNumber> <PhoneNumber>415-555-2001</PhoneNumber> </Employee> 
</Employees> 

图20-12演示了这个简单XML树的层次结构。

示例XML树的层次结构

XML类

LINQ To XML 可以以两种方式用于XML。第一种方式是作为简化的XML操作API,第二种
方式是使用本章前面看到的LTNQ查询工具。先介绍LINQ to XMLAPI。

LINQ to XML API由很多表示XML树组件的类组成。我们会使用的3个最重要的类包括
XElement、XAttribute和XDocument。当然,还有其他类,但这些是主要的。

从图20-12中可以看到,XML树是一组嵌套元素。图20-13演示了用于构造化树的类以
及它们如何被嵌套。
例如,图20-13显示了如下内容。

  • XDOCument节点可
    • 下面的每一个节类型,最多有一个:XDeclaration节点、XDocumentType节点以及
      XElement节点。
    • 任何数量的XProcessingInstruction节点。
  • 如果在XDocument下有最高级别的XElement节点,那么它就是树中其他元素的根。
  • 根元素可以包含任意数量的嵌套XElement、XComment或XProcessingInstruction节点,
    并且可以在任何级别上嵌套。

XML节点的容器结构
除了XAttribute类,大多数用于创建XML树的类都派生自一个叫作XNode的类,在文献中
也叫作"XNodes”。图20-13中XNode类显示为白色背景,XAttribute类显示为灰色背景。

创建、保存、加载和显示XML文档

演示化API的简单性和用途的最好方式就是展示一段简单的示例代码。例如,如下代码
演示了使用化后执行一些重要任务是多么简单。
从创建一个简单的包含一个Employees节点的XML树开始,两个子节点包含两个职员的名
字。注意代码的如下方面。

  • 树使用一条语句来创建,并同时在适当的位置创建所有的嵌套元素,这叫作函数式构造
    (functional construction)。

  • 每一个元素由对象创建表达式在适当的位置创建,使用了节点类型的构造函数。
    创建树之后,代码使用XDocument的Save方法把它保存在一个叫作EmployeesFi1e.xml的文
    件中。然后,再使用XDocument的Load静态方法把XML树从文件中重新读回,并把树赋值给一
    个新的XDocument对象。最后,使用WriteLine把新的XDocument对象保存的树结构显示出来。

using System;    //需要的命名空间
using System.Xml.Linq;class Program
{static void Main(){XDocument employees=new XDocument(                  //创建XML文档new XElement("Employees",   //创建根元素new XElement("Name","Bob Smith"), //创建元素new XElement("Name","Sally Jones") //创建元素)  );employees1.Save("EmlpoyeesFile.xml");   //保存到文件//将保存的文档加载到新变量中XDocument employees2=XDocument.Load("EmployeesFile.xml");Console.WriteLine(employees2);}
}

创建XML树

在之前的示例中,我们己经知道了能通过使用XDocument和XElement的构造函数在内存中创
建一个文档。在这里,对于两个构造函数:

  • 第一个参数都是对象名;
  • 第二个参数以及之后的参数包含了XML树的节点。构造函数的第二个参数是一个params
    参数,也就是说可以有任意多的参数。
    例如,如下代码产生了一个XML树并且使用Console.WriteLine方法来显示:
using System;    //此命名空间是必须的
using System.Xml.Linq;class Program
{static void Main(){XDocument employeeDoc=new XDocument(                           //创建文档new XElement("Employees"),       //创建根元素new XElement("Employee"),    //第一个employee元素new XElement("Name","Bob Smith"),new XElement("PhoneNumber","408-555-1000"),)new XElement("Employee",       //第二个employee元素new XElement("Name","Sally Jones"),new XElement("PhoneNumber","415-555-2000"),new XElement("PhoneNumber","415-555-2001"))));Console.WriteLine(employeeDoc);  //显示文档}
}

使用XML树的值

当我们遍历XML树来获取或修改值时,XML的强大就会体现出来。表20-2列出了用于获
取数据的主要方法。

表20-2查询XML的方法

续表

关于表20-2中的方法,需要了解的一些重要事项如下所示。

  • Nodes Nodes方法返回IEnumerable<0bject>类型的对象,因为返回的节点可能是不同的
    类型,比如XElement、XComment等。我们可以使用以类型作为参数的方法0fType(type)
    来指定返回某个类型的节点。例如,如下代码只获取节点:
IEnumerable<XComment> comments = xd.Nodes().OfType<XComment>(); 
  • Elements由于获取XElements是一个非常普遍的需求,就出现了Nodes.0fType(XElement)()
    表达式的简短形式一一Elements方法
    • 使用无参数的E1ements方法返回所有子XE1ement0
    • 使用单个name参数的E1ements方法只返回具有这个名字的子XE1ementso例如,如下
      代码行返回所有具有名字PhoneNumber的子XE1ement节点。
IEnumerable<XElement> empPhones = emp.Elements("PhoneNumber"); 
  • Element这个方法只获取当前节的第一个子与Elements方法相似,它可以
    带一个参数也可以不带参数调用。如果没有参数,它获取第一个子XElement节点。如果
    带一个姓名参数,它获取第一个具有该名字的子XElement。
  • Descendants和Ancestors这些方法与Elements和Parent方法差不多,只不过它们不返
    回直接的子元素或父元素,而是忽略嵌套级别,包括当前节点之下或者之上的所有节点。
    如下代码演示了Element和Elements方法:
using System;
using System.Collectiosn.Generic;
using System.Xml.Linq;class Program
{static void Main(){XDocument employeeDoc=new XDocument(new XDocument("Employees",new XElement("Employee",new XElement("Name","Bob Smith"),new XElement("PhoneNumber","408-555-1000")),new XElement("Employee",new XElement("Name","Samlly Jones"),new XElement("PhoneNumber","415-555-2000"),new XElement("PhoneNumber","415-555-2001"))));   //获取第一个名为"Employees”的子XElementXElement root=employeeDoc.Employee("Employees");IEnumerable<XElement>employees=root.Employees();foreach(XElement emp in employees){XElement empNameNode=emp.Employee("Name");//emp.Employee("Name")获取第一个名为"Name"的子XElementConsole.WriteLine(empNameNode.Value);IEnumerable<XElement>empPhones=emp.Elements("PhoneNumber");foreach(XElement phone in empPhones)Console.WriteLine($"{phone.Value}");}}
}

增加节点以及操作XML

我们可以使用Add方法为现有元素增加子元素。Add方法允许我们在一次方法调用中增加任
意多个元素,不管增加的节点类型是什么。

例如,如下的代码创建并显示了一个简单的XML树,然后使用Add方法为根元素增加单个
节点。之后,它再次使用Add方法来增加3个元素:两个XE1ements和一个XComment。注意输出
的结果:

using System;
using System.Xml.Linq;class Program
{static void Main(){XDocument xd=new XDocument(                 //创建XML树new XElement("root,new XElement("first")"));Console.WriteLine("Original tree");Consoel.WriteLine(xd);Console.WriteLine();//显示树XElement rt=xd.Element("root");       //获取第一个元素rt.Add(new XElement("second"));       //添加子元素rt.Add(new XElement("third"),new XDocument("Important Comment"),new XElement("fourth"));Console.WriteLine("Modified tree");Console.WriteLine(xd);                //显示Modeified tree}
}

Add方法把新的子节点放在既有子节点之后,但把节点放在子节点之前或者之间也是可以的,
使用AddFirst、AddBeforeSelf和AddAfterSelf方法即可。

表20-3列出了最重要的一些操作XML的方法。注意,某些方法针对父节点而其他一些方法
针对节点本身。

使用XML特性

特性提供了有关XElement节点的额外信息,它放在元素的开始标签中。
当我们以函数方法构造树时,在构造函数中包含XAttribute构造函数就可以
增加特性。XAttribute构造函数有两种形式,一种接受name和value,另一种接受现有XAttribute
的引用。

如下代码为root增加了两个特性。注意,提供给XAttribute构造函数的两个参数都是字符
串,第一个指定了特性名称,而第二个指定了值。

XDocument xd=new XDocument(new XElement("root",new XAttribute("Color","red"),      //特性构造函数new XAttribute("size","large"),     //特性构造函数new XElement("first"),new XElement("second"))
);Consoel.Writeline(xd);

这段代码产生了如下的输出。注意,特性放在了元素的开始标签中。

要从一个XElement节点获取特性可以使用Attribute方法,提供特性名称作为参数即可。下
面的代码创建了在一个节点中有两个特性(color和size)的XML树,然后从特性获取值并且
显示出来。

static void Main()
{XDocument xd=new XDocument(                  //创建XML数new XElement("root",new XAttribute("color","red"),new XAttribute("size","large"),new XElement("first")));Console.WriteLine(xd);Console.WriteLine();    //显示XML树XElement rt=xd.Element("root");   //获取元素XAttribute color=rt.Attribute("color");  //获取特性XAttribute size=rt.Attribute("size");    //获取特性Console.WriteLine($"color is {color.Value}");  //显示特性价Console.WriteLIne($"size is{size.Value}");     //显示特性值
}

要移除特性,可以选择一个特性然后使用Remove方法,或在它的父节点中使用set-
AttributeValue方法把特性值设置为null。下面是两种方法的演示:

static void Main()
{XDocument xd=new XDocument(new XElement("root",new XAttribute("color","red"),new XAttribute("size","large"),new XElement("first")));XElement rt=xd.Element("root");   //获取元素rt.Attribute("color").Remove();   //移除color特性rt.SetAttributeValue("size",null); //移除size特性Console.WriteLine(xd);
}

要从一个节点获取特性可以使用Attribute方法,提供特性名称作为参数即可。下
面的代码创建了在一个节点中有两个特性(color和size)的XML树,然后从特性获取值并且
显示出来。

static void Main()
{XDocument xd=new XDocument(                //创建XML树new XElement("root",new XAttribute("color","red"),new XAttribute("size","large"),new XElement("first")));Console.WriteLine(xd);Console.WriteLine();//显示XML树XElement rt=rt.Attribute("color");    //获取特性XAttribute size=rt.Attribute("size"); //获取特性Console.WriteLine($"color is {color.Value}");  //显示特性值Console.WriteLine($"size is {size.Value}");    //显示特性值
}

要移除特性,可以选择一个特性然后使用Remove方法,或在它的父节点中使用set-
AttributeValue方法把特性值设置为null。下面是两种方法的演示:

static void Main()
{XDocument xd = new XDocument(new XElement("root",new XAttribute("color", "red"),new XAttribute("size", "large"),new XElement("first")));XElement rt = xd.Element("root");     //获取元素rt.Attribute("color").Remove();       //移除color特性rt.SetAttributeValue("size", null);   //移除size特性Console.WriteLine(xd);
}

要向XML树增加一个特性或改变特性的值,可以使用setAttributeValue方法,如下代码
所示:

static void Main()
{XDocument xd=new XDocument(new XElement("root",new XAttribute("color","red"),new XAttribute("size","large"),new XElement("first")));XElement rt=xd.Element("root");    //获取元素rt.SetAttributeValue("size","mdeium");   //改变特性值rt.SetAttributeValue("width","narrow");  //添加特性Console.WriteLine(xd);Console.WriteLine();
}

其他类型的节点

前面示例中使用的其他3个类型的节点是XComment、XDeclaration以及XProcessinglnstruction,
如下描述。

XComment

XML注释由记号之间的文本组成0记号之间的文本会被XML解析器忽略。可以
使用XComment类向一个XML文档插人文本,如下面的代码行所示:

new XComment("This is a comment')

这段代码产生如下的文档行:

<!--This is a comment-->

XDeclaration

XML文档从包含XML使用的版本号、使用的字符编码类型以及文档是否依赖外部引用的一
行开始。这是有关的信息,因此它其实是有关元数据的元数据。这叫作XML声明,可以
使用XDeclaration类来插人。如下代码给出了一个XDeclaration语句的示例:

new XDeclaration("1.O","utf-8","yes")

这段代码产生如下的XML文档行:

<?xmlversion="1.0" encoding="utf-8" standalone="yes"?>

XProcessinglnstruction

XML处理指令用于提供关于文档的使用和解释方式的额外数据。处理指令最常用于关
联文档和样式表。

可以使用XProcessing lnstruction构造函数来包含处理指令。它接受两个字符串参数:目标和
数据串。如果处理指令接受多个数据参数,这些参数必须包含在XProcessingInstruction构造函
数的第二个字符串参数中,如下面的构造函数代码所示。注意,在这个示例中,第二个参数是一
个逐字字符串,在字符串中的双引号文本使用两个连续的双引号来表现。

new XProcessingInstruction("xml-stylesheet",@"href=""stories"",type=""text/csss""")

这段代码产生如下的XML文档行:

<?xml-stylesheet href="stories.css" type="text/css"?>

如下代码使用了所有的3个构造函数。

static void Main()
{XDocument xd=new XDocument(new XDeclaration("1.0","utf-8","yes"),new XComment("This is a comment"),new XProcessingInstruction("xml-stylesheet",@"href=""stories.css""type=""text/css"""),new XElement("root",new XElement("first"),new XElement("second")));
}

使用凵NQ to XML的LINQ查询

现在,我们可以把LINQ XML API和LINQ查询表达式组合在一起来产生简单而强大的
树搜索。

下面的代码创建了一个简单的化树,并显示在了屏幕上,然后把它保存在一个叫作
SimpleSample.xml的文件中。尽管代码没有什么新内容,但是我们会将这个XML树用于之后的
示例。

static void Main()
{XDocument Xd=new XDoCument(new XElement("MyElents",new XElement("first",new XAttribute("color","red"),new XAttribute("size","small")),new XElement("Sicond",new XAttribute("color","red"),new XAttribute("size","medium")),new XElement("third",new XAttribute("color","blue"),new XAttribute("size","large"))));Console.WrtiteLien(xd);xd.Save("SimpleSample.xml");
}

如下示例代码使用了简单的LTNQ查询来从XML树中选择节点的子集,然后以各种方式进
行显示。这段代码做了如下的事情。

  • 它从XML树中选择那些名字有5个字符的元素。由于这些元素的名字是first、second
    和third,只有first和third这两个名字符合搜索标准,因此这些节点被选中。
  • 它显示了所选元素的名字。
  • 它格式化并显示了所选节点,包括节点名以及特性值。注意,特性使用Attribute方法来
    获取,特性的值使用value属性来获取。
static void Main()
{XDocument xd = XDocument.Load("SimpleSample.xml");  //加载文档XElement rt = xd.Element("MyElements");             //获取根元素var xyz = from e in rt.Elements()                     //选择名称包含where e.Name.ToString().Length == 5         //5个字符的元素select e;foreachXElement x in xyz)Console.WriteLine(x.Name.ToString());             //显示所选的元素Console.WriteLine();foreach (XElement x in xyz)Console.WriteLine("Name:{0},color:{1},size:{2}",x.Name,x.Attribute("color").Value,x.Attribute("size").Value);
}

如下代码使用了一个简单的查询来获取XML树的所有顶层元素,并且为每一个元素创建了
一个匿名类型的对象。第一个WriteLine方法显示匿名类型的默认格式化,第二个WriteLine语
句显式格式化匿名类型对象的成员。

using System;
using System.Linq;
using System.Xml.Linq;static void Main()
{XDocument xd=XDocument.Load("SimpleSample.xml");    //加载文档XElement rt=xd.Element("MyElements");               //获取根元素var xyz=from e in rt.Elements()select new{e.Name,color=e.Attribute("color")};foreach(var x in xyz)Console.WriteLine(x);Console.WriteLine();forach(var x in xyz);Console.WriteLine("{0-6},color:{1,-7}",x.Name,x.color.Value);
}

从这些示例中可以看到,可以轻易地组合API和LNQ查询工具来产生强大的XML查
询能力。

http://www.dtcms.com/a/312376.html

相关文章:

  • CAP 理论笔记
  • CUDA杂记--nvcc使用介绍
  • GitHub 趋势日报 (2025年08月02日)
  • 控制建模matlab练习07:比例积分控制-③PI控制器的应用
  • 深入掌握 ExcelJS:Node.js 中强大的 Excel 操作库
  • 小红书开源dots.ocr:单一视觉语言模型中的多语言文档布局解析
  • WebRTC前处理模块技术详解:音频3A处理与视频优化实践
  • ⭐CVPR2025 3D 生成新框架|Kiss3DGen 让 2D 扩散模型玩转 3D 资产生成
  • sqli-labs:Less-26关卡详细解析
  • 【数据迁移】Windows11 下将 Ubuntu 从 C 盘迁移到 D 盘
  • Spring Boot 的事务注解 @Transactional 失效的几种情况
  • MCU中的复位生成器(Reset Generator)是什么?
  • 智能手表项目:原理图
  • kotlin kmp 跨平台环境使用sqldelight
  • Shell脚本-变量如何定义
  • webrtc弱网-QualityScaler 源码分析与算法原理
  • npm ERR! code CERT_HAS_EXPIRED:解决证书过期问题
  • `npm error code CERT_HAS_EXPIRED‘ 问题
  • Azure DevOps — Kubernetes 上的自托管代理 — 第3部分
  • JVM-垃圾回收器与内存分配策略详解
  • Node.js 服务可以实现哪些功能
  • 【python实用小脚本-169】『Python』所见即所得 Markdown 编辑器:写完即出网页预览——告别“写完→保存→刷新”三连
  • 深度学习周报(7.28~8.3)
  • 【机器学习③】 | CNN篇
  • 分享链接实现状态共享
  • 嵌入式相关书籍
  • Javaweb————Windows11系统和idea2023旗舰版手动配置Tomcat9全流程解析
  • FreeRTOS源码分析三:列表数据结构
  • MCP革命:Anthropic如何重新定义AI与外部世界的连接标准
  • Linux系统编程Day4-- Linux常用工具(yum与vim)