1、了解SGML
SGML(Standard Generalized Markup Language)是一种标准通用标记语言,它是
HTML 和 XML 的前身。SGML 是一种元标记语言,它定义了一套规则和语法,用于创建其
他标记语言,如 HTML 和 XML。
SGML 的主要目标是提供一种通用的标记语言,使用户能够定义自己的标记语言,并
根据自己的需求来定义文档结构和标记。SGML 的语法非常灵活,可以根据需要定义标记、
元素和属性,并允许用户创建自定义实体和文档类型定义(DTD)。
SGML 的主要特点包括:
分离内容和表示:
SGML 允许将文档的结构和内容与其呈现方式分开。这使得文档可以在不同的环境中
以不同的方式呈现,而不需要修改文档的内容。
可扩展性:
SGML 允许用户根据自己的需求来定义标记和文档结构。这使得用户可以根据自己的
需求来创建自定义的标记语言和文档类型。
复用性:
SGML 允许用户定义实体和实体引用,以便在多个文档中共享和重用内容片段。这提
高了文档的可维护性和可重用性。
尽管 SGML 是一种强大的标记语言,但它的语法复杂且庞大,不太适合直接使用。
因此,HTML 和 XML 这样的子集标记语言被开发出来,以简化和约束 SGML 的语法,使
其更易于使用和理解。
2、XML术语:
XML 使用标签、节点、元素、子元素、后代元素、属性、命名空间和字符数据来描
述和组织数据。
Harry Potter
J.K. Rowling
The Lord of the Rings
J.R.R. Tolkien
HarperCollins
标签(Tag):
XML中的标签是用尖括号< >包围的名称,用于标识元素或节点。标签可以是开始标
签
结点(Node):
XML中的结点是XML文档中的基本单位。结点可以是元素、属性、注释、处理指令等。
元素(Element):
XML 中的元素是由开始标签和结束标签组成的,它可以包含其他元素、文本和属性。
元素由标签和内容组成,例如
子元素(Child Element):
XML 中的子元素是位于父元素内部的元素。例如,在
素是
后代元素(Descendant Element):
XML中的后代元素是指位于某个元素下方的所有元素,无论它们是直接子元素还是进
一步嵌套的子元素。
属性(Attribute):
XML中的属性是元素的附加信息,用于提供关于元素的额外数据。属性由名称和值组
成,例如
命名空间(Namespace):
XML 中的命名空间用于区分具有相同名称的元素和属性。命名空间通过使用前缀来
标识,例如
字符数据(Character Data):
XML中的字符数据是指文本内容,它可以是元素的文本内容、注释或处理指令等。例
如,在
CDATA 是 XML 中的一种特殊语法,用于将包含特殊字符(如<和>)的文本数据转义,
以避免被解析为标签或其他特殊语法。
不会被解析为标签,而是作为纯文本数据进行处理。
注意,在CDATA块中,仅有]]>这个字符序列需要进行特殊处理,因为它会被误认
为是 CDATA 结束标记。为了避免这种情况,可以使用]]]]> 这样的技巧,
将]]>分成两个块。
问:上面的]]>造成的误会是什么情况?
答:在CDATA块中,只有]]>这个字符序列会被误认为是CDATA结束标记。因此,如果CDATA
块中包含]]>这个字符序列,解析器会错误地将其视为CDATA块的结束,从而导致解析错误。
data]]>
上面CDATA块中的文本是"This is some ]]> data"。由于]]>这个字符序列出现在
CDATA块中,解析器会错误地将其视为CDATA块的结束标记,导致解析错误。
为了避免这种情况,可以使用技巧将]]>分成两个块。例如:
data]]>
上面CDATA块被分成了两个部分:"This is some ]]]>" 和 "> data"。这
样可以避免解析器将]]>视为CDATA块的结束标记,确保CDATA块中的文本正确解析。
总结:如果CDATA块中包含 ]]> 这个字符序列,解析器会错误地将其视为CDATA块的结
束标记,导致解析错误。为了避免这种情况,可以使用技巧将 ]]> 分成两个块。
3、了解RSS
RSS(Really Simple Syndication)是一种用于发布和订阅网站内容的标准格式。它
是一种基于 XML 的格式,用于将网站的更新内容以结构化的方式传递给订阅者。
RSS 的主要用途是让用户能够方便地获取他们感兴趣的网站内容的最新更新,而无需
浏览每个网站。通过订阅 RSS 源,用户可以使用 RSS 阅读器或其他应用程序来自动接收
和阅读网站的最新内容。
RSS 的工作原理如下:
1. 网站所有者将网站的内容以 RSS 格式发布到一个特定的 URL 上,称为 RSS 源。
2. 用户使用 RSS 阅读器订阅感兴趣的 RSS 源。
3. 当网站的内容有更新时,网站所有者会将更新的内容发布到 RSS 源上。
4. 用户的 RSS 阅读器会定期检查所订阅的 RSS 源,如果有新内容,则会自动下载
并显示给用户。
RSS 的优点和用途包括:
1. 方便订阅:用户可以通过订阅 RSS 源来获取多个网站的内容更新,而无需逐个访
问每个网站。
2. 及时获取信息:用户可以实时获取感兴趣网站的最新内容,无需等待推送或手动
查找。
3. 自定义内容:用户可以根据自己的兴趣和需求选择订阅的 RSS 源,只接收感兴趣
的内容。
4. 跨平台和设备:RSS 阅读器可以在不同平台和设备上使用,如桌面电脑、手机和
平板电脑等。
总结起来,RSS 是一种用于发布和订阅网站内容的标准格式,通过订阅 RSS 源,用户
可以方便地获取感兴趣网站的最新内容更新,提高信息获取效率。
4、问:如何利用notpad++直接调用浏览器进行查看xml或html?
答:每次切换查看效果非常烦。可直接用notpad++调用浏览器来查看效果。
notpad++中点击运行-运行,在命令中输入下面格式:
浏览器全路径+空格+htm或xml的全路径
然后点保存,设置好快捷键。这里,我设置为Ctrl+F5,每次编好后,按快捷键直接
查看。
问:XML中有
答:
一般XML标签成对出现,对于没有任何内容的,可以象上面简写,表示自关闭标签,
没有任何内容。相当于
问:属性用单引号时,并没有报错,何故?
答:那是浏览器兼容,如果是另一个浏览器,可能就报错了。而且大多解析XML的都是按
规范标准(双引号)进行解析。因此,对于属性,请严格按规范使用双引号来做。
问:如何注释?
答:用进行注释。尽管可能浏览器要显示它,但它并不是一个结点或标签.
问:HTML 的转义符在 XML 中不能直接使用?
答:是的,尽管有些通用,但并不尽然。注意对比:
XML转义符:
&(和号):&
<(小于号):<
>(大于号):>
"(双引号):"
'(单引号):'
htm转义符:
<:小于号 <
>:大于号 >
&:和号 &
":双引号 "
':单引号 '
:非断行空格
©:版权符号 ©
®:注册商标符号 ®
€:欧元符号 €
£:英镑符号 £
¥:日元符号 ¥
如果把html的注册商标®用到XML中将报错。
5、一般html标签推荐使用小写,但XML因大小写敏感没有此项约束。
HTML 标签的大小写不会影响代码的编译和执行,意味着您可以使用任何大小写组合
编写 HTML 标签,而代码的行为不会受到影响。
然而,根据 HTML 的规范和最佳实践,推荐将 HTML 标签全部使用小写字母。这是因
为 HTML 标签在规范中被定义为小写字母,并且大多数开发者和工具都遵循这个约定。使
用小写标签可以提高代码的可读性,并且与其他开发者的代码保持一致,使代码更易于理
解和维护。
string html = @"
My Web Page
Welcome to my web page
This is a paragraph of text.
";
对于 XML,大小写是敏感的,而且没有强制要求标签必须使用小写字母。根据 XML
的规范,标签名称可以使用大小写字母的任何组合。
所以,在编写 XML 文件时,您可以根据个人偏好选择使用大写字母、小写字母或混
合大小写。然而,为了与常见的 XML 规范和最佳实践保持一致,推荐使用小写字母来编
写 XML 标签。
John Doe
30
Jane Smith
25
6、问:为什么网站在传输大量数据时,一般使用json而不是xml?
答:XML 格式的标签嵌套和冗余可能会导致处理速度较慢。XML 解析器需要遍历整个文档
树来解析和提取数据,这可能会导致性能下降。
JSON 格式具有更简洁的结构,相对于XML格式来说,它更容易解析和处理。JSON的数
据存储方式是键值对,可以通过键来快速访问和筛选数据。C#中的Newtonsoft.Json库提
供了高效的 JSON 解析和操作方法,可以快速提取和处理 JSON 数据。
CSV 或以文本为分隔符的格式,它们具有简单的结构和较小的文件大小,因此解析
和处理速度通常较快。C# 中的内置 StreamReader 和 Split 方法可以轻松地解析和处
理这些格式。
对于CSV或文本的数据通常需要手动解析和处理每一行,将其转换为对象或数据结
构,然后使用LINQ查询语法来对这些对象进行查询和操作。
简言之:选择已经存在多种格式的同数据文件,若需要筛选、统计等功能,最好选
JSON,以方便C#直接使用Linq操作。
1、DOM(文档对象模型)
DOM是一种用于访问和操作HTML或XML文档的编程接口。DOM提供了一种将文档表示为对象的
方式,使开发者可以通过编程方式修改文档的结构、内容和样式。
使用DOM进行HTML或XML文档的操作也存在一些缺点,内存消耗(整个文档加载到内存)、
性能问题(性能不如其他处理方式,如SAX解析器)、执行复杂操作的复杂性(可能需要编写
较多的代码进行节点间的导航和操作)、没有验证功能(不会对文档的有效性进行验证,可
能导致错误或意外行为)、不适用于非标准的HTML/XML结构(非标准的HTML或XML结构,DOM
操作可能会遇到困难)
尽管如此,但它操作的方便性掩盖它的缺点。
语法:
获取DOM对象:可以通过引用合适的命名空间(如System.Xml或System.Html)来创建
DOM对象,例如XmlDocument或HtmlDocument。
导航DOM树:使用节点之间的关系进行导航,例如使用父节点、子节点、兄弟节点等
属性来获取和操作节点。
属性:
InnerText:获取HTML或XML节点的文本内容。多结点时各文本串接成整体。
InnerHtml:获取或设置HTML节点的内部HTML代码(含文本)。只有子结点。
OuterHtml:获取或设置HTML节点及其子节点的HTML代码。
Attributes:获取节点的属性集合,可以通过此属性来访问和操作节点的属性。
方法:
GetElementsByTagName:根据标签名称获取节点的集合。
GetElementById:根据元素的ID获取一个元素节点。
AppendChild:向父节点添加一个子节点。
RemoveChild:从父节点移除一个子节点。
SetAttribute:设置节点的属性值。
GetAttribute:获取节点的属性值。
AddEventListener:为节点添加事件处理程序。
问:XMLDocument与XDocument的区别?
答:XmlDocument和XDocument是两个不同的类,用于处理 XML 数据。区别:
1. 命名空间:XmlDocument是在System.Xml命名空间中定义的,而XDocument是在
System.Xml.Linq 命名空间中定义的。XDocument是Linq to XML的一部分,提供了更简
洁、更现代化的 API。
2. API 和语法:XmlDocument使用传统的DOM (Document Object Model) API,而
XDocument使用Linq to XML API。XDocument 提供了一组更简单、更直观的方法和语法
来处理 XML 数据,例如使用 LINQ 查询语句来查询和操作 XML。
3. 可读性:XDocument通常比XmlDocument更易于阅读和编写,因为它的语法更接
近于 XML 本身,更具可读性和可维护性。
4. 功能和性能:XmlDocument在功能和性能方面更加成熟和全面,它提供了广泛
的功能和方法来处理 XML 数据。而XDocument虽然在简洁性和可读性方面更有优势,
但在某些复杂的场景下可能会有一些性能上的损失。
XmlDocument是传统的XML处理类,提供了广泛的功能和方法,适用于复杂的XML处
理任务。而XDocument是Linq to XML的一部分,提供了更简洁、更现代化的 API,适用
于简单和中等复杂度的XML处理任务。选择使用哪个类取决于具体的需求和个人偏好。
Sax(是Java事件驱动,读写XML),.net中使用XmlReader(XmlTextReader)、
XmlWriter(XmlTextWriter)来替代。
XmlReader与XmlWriter是基于流的、轻量级的XML解析器,它按顺序读写XML数据,并
通过事件通知应用程序处理不同的节点。与SAX解析器类似,XmlReader不会将整个XML文档
加载到内存中,而是以流的方式逐个节点解析。因此,可能会导致代码变得冗长和繁琐。
这种逐节点处理的方式可能不适合处理复杂的 XML 结构或需要频繁跳转和操作的情况。
2、XMLSerializer序列化器(复习)
XmlSerializer类是一个用于将对象序列化为XML格式的序列化器。它可以将对象的公共字
段和属性转换为XML元素,并将对象的值转换为XML元素的内容。
internal class Program
{
private delegate void Mydelegate(string s);
private static void Main(string[] args)
{
Person p = new Person() { Name = "基金", Age = 5 };
XmlSerializer xmlser = new XmlSerializer(typeof(Person));
StringWriter swriter = new StringWriter();
xmlser.Serialize(swriter, p);
string sp = swriter.ToString();
Console.WriteLine(sp);
StringReader sreader = new StringReader(sp);
Person p1 = (Person)xmlser.Deserialize(sreader);
Console.WriteLine(p1.Name);
Console.ReadKey();
}
}
public class Person
{
public int Age { get; set; } = 19;
public string Name { get; set; } = "刀郎";
}
创建了一个XmlSerializer对象,并使用其Serialize方法将Person对象序列化为XML,并将
其写入一个StringWriter。然后,我们从StringWriter中获取序列化后的XML字符串,并输
出它。然后创建了一个StringReader来读取XML字符串,并使用XmlSerializer的Deserialize
方法将其反序列化为Person对象。最后,我们输出反序列化后的Person对象的属性值。
StringWriter与StringReader介绍
StringWriter 和 StringReader 提供了一种在内存中读取和写入文本的方式,适用于
需要将文本作为字符串进行处理的情况,如字符串操作、单元测试、日志记录和数据序列
化。它们提供了更方便和灵活的方法,而不需要实际的文件或流。
字符串操作:StringWriter 和 StringReader 提供了一种方便的方式,可以将文本数
据作为字符串进行处理。您可以使用StringWriter将文本写入字符串,并使用StringReader
从字符串中读取文本。这对于需要对文本进行操作、处理或传递的情况非常有用。
单元测试:StringWriter 和 StringReader 在编写单元测试时非常有用。您可以使用
StringWriter 捕获方法或函数中生成的文本输出,并将其与预期结果进行比较。同样,您
可以使用 StringReader 从字符串中读取预定义的输入,以便在测试过程中模拟用户输入。
日志记录:StringWriter 可以用于在内存中构建日志消息,而不是将其写入实际的日
志文件或数据库。这对于在应用程序中生成和处理日志消息时非常有用,特别是在开发和
调试阶段。
数据序列化:StringWriter和 StringReader可以与其他序列化机制(如JSON或XML)
一起使用,以便将对象序列化为字符串或从字符串中反序列化对象。您可以使用StringWriter
将对象序列化为字符串表示,然后使用 StringReader 从字符串中读取并反序列化对象。
StringWriter 类
StringWriter 类继承自TextWriter类,而TextWriter类是用于写入字符的抽象基类。
StringWriter类重写了TextWriter中的一些方法,以便将字符写入到内部的字符串缓冲区
中。意味着 StringWriter 内部使用的是字符缓冲区,而不是字节流。它提供了一种方便
的方式来将字符写入到字符串中,而不需要关注底层的字节流。
属性:
Encoding:获取用于写入字符串的编码。
方法:
Write:将指定的字符串写入 StringWriter。
WriteLine:将指定的字符串和换行符写入 StringWriter。
Flush:清空 StringWriter 的缓冲区,将缓冲区的内容写入字符串。
ToString:将 StringWriter 中的内容转换为字符串。
using (StringWriter writer = new StringWriter())
{
writer.WriteLine("Hello");
writer.WriteLine("World");
string result = writer.ToString();
Console.WriteLine(result);
}
StringReader类
StringReader 类继承自TextReader类,而TextReader类是用于读取字符的抽象基类。
故,它用的是字符缓冲区,而不是字节流。
方法:
Peek:返回下一个可用字符,但不会将其从输入流中读取。游标不会移动
Read:读取输入流中的下一个字符,并将该字符作为一个整数返回。游标移动.
ReadBlock:从输入流中读取指定数量的字符,并将它们存储在缓冲区数组中。
ReadLine:从输入流中读取一行字符,并返回该行字符的字符串表示形式。
ReadToEnd:从当前位置到末尾读取输入流中的所有字符,并返回它们的字符串表
示形式。
string s = "Hello World!";
using (StringReader srd = new StringReader(s))
{
int n;
while ((n = srd.Read()) != -1)
{
Console.WriteLine((char)n);
}
Console.WriteLine(srd.ReadToEnd());//a
}
因为没有游标重置的方法,所以a处并不会输出内容。若要重置只能重新定义StringReader.
3、小结
读写XML有很多技术:
1.Dom[XmlDocument、XDocument](文档对象模型,将整个xml加载到内存中,然后操作);
2.Sax(是Java事件驱动,读写XML),.net中使用XmlReader(XmlTextReader)、
XmlWriter(XmlTextWriter)来替代,还有高级的读写技术;
3.XmlSerializer(xml序列化,需要先定义类);
4.Linq To XML(System.Xml.Linq)等。
XmlSerializer要求对每种不同的文件都定义一套类,很麻烦,而Linq To XML则不需
要单独创建类,当然更底层一些,代码比XmlSerializer多,灵活性更高。
System.Xml下的类是2.0及之前操作xml推荐的,现在很多人也仍然在用这个namespace
下的类,这个namespace下的类和Linq To XML非常相似,因此不用单独学。
核心类XElement,一个XElement表示一个元素,new XElement(“Order”),创建一个名
字为Order的标签,调用Add增加子元素,也是XElement 对象,和TreeView一样。
想得到字符串怎么办?ToString
调用XElement的Save方法将xml内容保存在Writer中
创建xml的时候可以用XDocument也可以不用。(直接用XElement)
练习:接Dom建立下面的XML
黄林
18
许正龙
19
程序如下:
XmlDocument xmlDoc = new XmlDocument();
//声明
XmlDeclaration xmlDec = xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", "yes");//a
xmlDoc.AppendChild(xmlDec);
//根结点
XmlElement xmlRoot = xmlDoc.CreateElement("root");
xmlDoc.AppendChild(xmlRoot);
//班结点
XmlElement xmlClass = xmlDoc.CreateElement("class");
XmlAttribute attr = xmlDoc.CreateAttribute("id");
attr.Value = "c01";
xmlClass.Attributes.Append(attr);
xmlRoot.AppendChild(xmlClass);
//学生1结点
XmlElement xmlStu = xmlDoc.CreateElement("student");
attr = xmlDoc.CreateAttribute("sid");
attr.Value = "s011";
xmlStu.Attributes.Append(attr);
xmlClass.AppendChild(xmlStu);
//name,age结点
XmlElement xmlName = xmlDoc.CreateElement("name");
xmlName.InnerText = "黄林";
xmlStu.AppendChild(xmlName);
XmlElement xmlAge = xmlDoc.CreateElement("age");
xmlAge.InnerText = "18";
xmlStu.AppendChild(xmlAge);
//学生2结点
xmlStu = xmlDoc.CreateElement("student");
attr = xmlDoc.CreateAttribute("sid");
attr.Value = "s012";
xmlStu.Attributes.Append(attr);
xmlClass.AppendChild(xmlStu);
xmlName = xmlDoc.CreateElement("name");
xmlName.InnerText = "许正龙";
xmlStu.AppendChild(xmlName);
xmlAge = xmlDoc.CreateElement("age");
xmlAge.InnerText = "19";
xmlStu.AppendChild(xmlAge);
//保存或显示
xmlDoc.Save("school.xml");
Console.WriteLine("OK");
逐个增加元素,在每一个元素增加下级元素。
增加声明XmlDocument.CreateXmlDeclaration(version,encoding,standalone)中:
standalone(是否独立)表示XML文档是否是独立的。常见的值包括"yes"和"no"。该
参数是一个字符串类型。
"yes":表示 XML 文档是独立的,即它不依赖于任何外部资源。
"no":表示 XML 文档不是独立的,即它依赖于外部资源。
当 XML 文档被视为独立的时候,解析器可以完全加载和解析整个文档,而不需要引
用或访问其他外部资源。这意味着解析器可以在没有网络连接或访问权限的情况下,完
全处理该文档。
当 XML 文档被视为非独立的时候,解析器可能需要引用或访问其他外部资源,例如
DTD(文档类型定义)文件或外部实体。这些外部资源可能包含文档中使用的实体、元素
或属性的定义,或者提供文档的结构和验证规则。在这种情况下,解析器需要能够访问
这些外部资源,以正确解析和处理文档。
注意:standalone属性并不会自动加载或处理任何外部资源。它只是提供了一个指
示,告诉解析器文档是否是独立的。让程序员明白,解析器在处理文档时,可能需要使
用其他方法或设置来加载和处理外部资源。
简言之:只是一个标志,让人明白,是否还需要用其它来处理。
为了把上面的例子简写,使用循环写入,因此定义一个List
list中,再循环逐个写入每一个对象:
internal class Program
{
private static void Main(string[] args)
{
List lists = new List()
{
new Person{ Name= "黄林",Age = 18 },
new Person { Name = "许正龙", Age = 19 }
};
XmlDocument xmlDoc = new XmlDocument();
XmlDeclaration xmlDec = xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", null);
xmlDoc.AppendChild(xmlDec);
XmlElement xmlRoot = xmlDoc.CreateElement("list");
xmlDoc.AppendChild(xmlRoot);
for (int i = 0; i < lists.Count; i++)
{
XmlElement xmlPerson = xmlDoc.CreateElement("Person");
XmlAttribute attr = xmlDoc.CreateAttribute("id");
attr.Value = (i + 1).ToString();
xmlPerson.Attributes.Append(attr);
XmlElement xmlName = xmlDoc.CreateElement("name");
xmlName.InnerText = lists[i].Name;
xmlPerson.AppendChild(xmlName);
XmlElement xmlAge = xmlDoc.CreateElement("age");
xmlAge.InnerText = lists[i].Age.ToString();
xmlPerson.AppendChild(xmlAge);
xmlRoot.AppendChild(xmlPerson);
}
xmlDoc.Save("school.xml");
Console.WriteLine("OK");
Console.ReadKey();
}
}
public class Person
{
public int Age { get; set; }
public string Name { get; set; }
}
看似好像也少了一些处理,尽管有条理,但XMlDocument还是比较麻烦。
问:XmlElement与Xmlattribute作为特性时一样吗?
答:不一样,当字段或属性序列化生成XML时,前者为元素别名,后面为上级元素属性。
internal class Program
{
private static void Main(string[] args)
{
Person p = new Person() { Name = "Tom", Age = 8 };
XmlSerializer ser = new XmlSerializer(typeof(Person));
using (StringWriter sw = new StringWriter())
{
ser.Serialize(sw, p);
Console.WriteLine(sw.ToString());
}
Console.ReadKey();
}
}
public class Person
{
[XmlElement("yangling")]
public int Age { get; set; }
[XmlAttribute("Minzhi")]
public string Name { get; set; }
}
注意看一下结果:
8
XmlElementAttribute用于序列化对象属性为XML元素,而XmlAttribute用于序列化对象
属性为XML属性。
再次改写成XDocument来写入xml:
private static void Main(string[] args)
{
List lists = new List()
{
new Person{ Name= "黄林",Age = 18 },
new Person { Name = "许龙", Age = 19 }
};
XDocument xDoc = new XDocument();
//是属性,不再是add添加
xDoc.Declaration = new XDeclaration("1.0", "UTF-8", null);
//是单独的元素,而非doc下元素,new XElemnet进行创建,更符合逻辑
XElement xRoot = new XElement("list");
xDoc.Add(xRoot);
for (int i = 0; i < lists.Count; i++)
{
XElement xmlp = new XElement("person");
xmlp.SetAttributeValue("id", (i + 1).ToString());
xmlp.SetElementValue("name", lists[i].Name);
xmlp.SetElementValue("age", lists[i].Age);
xRoot.Add(xmlp);
}
xDoc.Save("school.xml");
Console.WriteLine("OK");
Console.ReadKey();
}
}
public class Person
{
public int Age { get; set; }
public string Name { get; set; }
}
问:XMLDocument与XDocument谁更高效?
答:一般是XDocumnet.
在处理 XML 数据时,XDocument 的性能通常比 XmlDocument 更高。
XDocument 是 LINQ to XML 的一部分,它提供了一种更现代和简化的方式来处理
XML 数据。XDocument 使用了更高效的内存管理和查询技术,因此在大多数情况下比传
统的 XmlDocument 更快。
1. 内存消耗:XDocument 使用了更紧凑的数据结构,因此在处理大型 XML 文档时,
它的内存消耗通常比 XmlDocument 更低。
2. 查询语言:XDocument 使用了 LINQ to XML,它提供了一种简洁而强大的查询语
言来操作 XML 数据。这种查询语言比传统的 XmlDocument 的 DOM 操作更高效。
3. 可读性:XDocument 的 API 设计更加简洁和直观,使得代码更易于理解和维护。
相比之下,XmlDocument 的 API 设计较为复杂和冗长。
然而,对于小型或简单的 XML 数据,差异可能不太明显。在某些情况下,
XmlDocument 可能更适合特定的需求,尤其是当需要对 XML 数据进行复杂的修改或操
作时。
因此,对于大多数情况,XDocument 是更高效的选择,特别是在处理大型 XML 文
档时。但是,具体的性能取决于具体的使用情况和需求,因此在选择时应根据实际情况
进行评估。
问:输入代码时查看方法重载的快捷键是多少?
答:当你调用方法并添加括号后,按下 Ctrl + Shift + Space。这将触发自动完成提
示,并显示方法的重载列表。你可以使用上下箭头键选择要查看的重载,选定后,直接
继续输入各参数即可。
在已经完成的代码中,鼠标定位在方法后面的括号中,按上面快捷键一样可查看。
4、读取XML(递归加载到TreeView)
下面用XDocument读取XML,因为是用的百度RSS,所以这个在网上方便练习,先用
WebClient同步从网上获取xml的字符串,然后再进行递归读取。
private void button1_Click(object sender, EventArgs e)
{
WebClient wc = new WebClient();
wc.Encoding = Encoding.UTF8;
string s = wc.DownloadString(@"http://news.baidu.com/n?cmd=1&class=civilnews&tn=rss&sub=0");
XDocument xDoc = XDocument.Parse(s);
XElement xRoot = xDoc.Root;
TreeNode tdRoot = treeView1.Nodes.Add(xRoot.Name.ToString());
LoadTreeViewByXDoc(xRoot, tdRoot.Nodes);
treeView1.ExpandAll();
tdRoot.EnsureVisible();
treeView1.SelectedNode = tdRoot.Nodes[0].Nodes[3];
treeView1.Focus();
}
private void LoadTreeViewByXDoc(XElement xRoot, TreeNodeCollection t)
{
foreach (XElement item in xRoot.Elements())
{
if (item.Elements().Count() == 0)
{
TreeNode tt = t.Add(item.Name.ToString());
if (item.Value.ToString() != "")
{
tt.Nodes.Add(item.Value.ToString());
}
}
else
{
TreeNode tt = t.Add(item.Name.ToString());
LoadTreeViewByXDoc(item, tt.Nodes);
}
}
}
上面网络获取xml部分也可以使用httpclient方式
HttpClient client = new HttpClient();
HttpResponseMessage resp = await client.GetAsync(@"http://news.baidu.com/n?cmd=1&class=civilnews&tn=rss&sub=0");
resp.EnsureSuccessStatusCode();
string s = await resp.Content.ReadAsStringAsync();
async表示异步,即各运行各自己的,不同调,不配合,每个人都自由运行。
await表示异步方法时会暂停执行已方,并将控制权返回给调用方。常与async配合,当异步
执行完成后,await将不再暂停而是继续从本句下面开始执行。
上面resp通过http协议,用getASync异步方法去请求,得到的响应从EnsuresuccessStatuscode
中取得响应情况,若出错就直接异常了。在正常响应开发部下,继续异步从服务器上去读取XML
以字符的形式返回。
当你从 Web 服务器获取响应时,服务器通常会在 HTTP 响应头中包含一个字符集
(charset)指定了响应内容的编码方式。ReadAsStringAsync() 方法会根据这个字符集信
息来解码响应的内容。
然而,有一些情况可能会出现问题。例如,如果服务器没有提供正确的字符集信息,或
者服务器提供的字符集信息与实际使用的编码不符,那么 ReadAsStringAsync() 方法可能
会无法正确解码响应的内容。在这种情况下,你可能需要手动指定正确的编码方式。如:
resp.EnsureSuccessStatusCode();
byte[] bytes = await resp.Content.ReadAsByteArrayAsync();
string s = Encoding.UTF8.GetString(bytes);
后面的是展开所的treeview,并定位到可以看到根结点,然后选中某结点并激活treeview
使被选中的结点蓝底白字。
下面使用XMLDocument进行递归加载
private void button1_Click(object sender, EventArgs e)
{
WebClient wc = new WebClient();
wc.Encoding = Encoding.UTF8;
string s = wc.DownloadString(@"http://news.baidu.com/n?cmd=1&class=civilnews&tn=rss&sub=0");
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(s);
XmlElement xmlRoot = xmlDoc.DocumentElement;
TreeNode nodeRoot = treeView1.Nodes.Add(xmlRoot.Name);
LoadToTreeViewByXmlDoc(xmlRoot, nodeRoot.Nodes);
treeView1.ExpandAll();
nodeRoot.EnsureVisible();
treeView1.SelectedNode = nodeRoot.Nodes[0].Nodes[3];
treeView1.Focus();
}
private void LoadToTreeViewByXmlDoc(XmlElement xmlRoot, TreeNodeCollection nodes)
{
foreach (XmlNode item in xmlRoot.ChildNodes)
{
if (item.NodeType == XmlNodeType.Element)
{
TreeNode node = nodes.Add(item.Name);
LoadToTreeViewByXmlDoc((XmlElement)item, node.Nodes);//结点强转为元素
}
else
{
if (item.NodeType == XmlNodeType.Text | item.NodeType == XmlNodeType.CDATA)
{//类型为文本或CDATA,就添加文本值。此时不再可能有子元素,递归终止条件。
nodes.Add(item.Value);
}
}
}
}
注意上面xmldocument没有直接的xmlelements使用,只能通过子节点(直接子结点,不
包括孙结点以后面的后代结点)来罗列,同时还必须通过nodetype判断结点类型。
例如
Text,第三个是endelement,类型不同,而且 判断时应好好利用nodetype.
NodeType(节点类型)
NodeType(节点类型)是用于标识 XML 文档中节点的不同类别或类型的枚举值。
XmlDocument 中,节点类型由 XmlNodeType 枚举表示。常见的节点类型:
1、XmlNodeType.Element:表示元素节点,用于表示 XML 标签和其内容。
例如:
2、XmlNodeType.Attribute:表示属性节点,用于表示元素的属性。
例如:id="123"、name="John"。
3、XmlNodeType.Text:表示文本节点,用于表示元素内的文本内容。
例如:Tom、Hello, World!。
4、XmlNodeType.CDATA:表示 CDATA 节点,用于表示包含特殊字符(如"")的文本内容。
例如:。
5、XmlNodeType.Comment:表示注释节点,用于表示 XML 文档中的注释。
例如:;。
6、XmlNodeType.ProcessingInstruction:表示处理指令节点,用于表示 XML 文档中
的处理指令。例如:;。
7、XmlNodeType.DocumentType:表示文档类型节点,用于表示 XML 文档的类型定义。
例如:;
8、XmlNodeType.EndElement:表示文档类型节点的结束标签节点。
例如:
注意:
使用 XmlNode.NodeType 属性可以获取节点的类型,该属性返回的是XmlNodeType
枚举值。
在处理节点时,通常需要根据节点的类型来进行针对性的操作,比如提取元素节点
的属性值、获取文本节点的文本内容等。
需要注意的是,节点类型的判断和处理应该结合 XML 文档的结构和语义进行,不
同类型的节点可能需要不同的处理逻辑。
5、访问某个结点
在 C# 的 XmlDocument 中,访问某个节点有以下几种常用的方法:
(1)通过节点的路径访问:
使用 SelectSingleNode() 方法:
通过使用 XPath 表达式来选择并返回符合条件的第一个节点。
使用 SelectNodes() 方法:
通过使用 XPath 表达式来选择并返回符合条件的节点集合。
注意:
XPath 表达式需要根据 XML 文档的结构和需求来编写。
如果要访问的节点是唯一的,可以使用 SelectSingleNode() 方法来获取单个节点。
如果要访问的节点可能有多个,可以使用 SelectNodes() 方法来获取节点集合。
(2)通过节点的名称访问:
使用 GetElementsByTagName() 方法:
通过指定节点的标签名称来获取所有具有该标签的节点集合。
使用 GetElementById() 方法:
通过节点的 ID 属性值来获取符合条件的节点(该节点的属性值需要在 XML 中声
明为 ID 属性)。
注意:
GetElementsByTagName() 方法返回的是一个 XmlNodeList,可以通过遍历该集合来访问
每个节点。
GetElementById() 方法返回的是单个节点,直接可以对返回的节点进行访问和操作。
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("example.xml");
// 使用 XPath 访问某个节点(通过路径)
XmlNode node1 = xmlDoc.SelectSingleNode("//root/element/name"); // 单个节点
XmlNodeList nodeList1 = xmlDoc.SelectNodes("//root/element"); // 节点集合
// 使用节点名称访问(通过标签名或属性)
XmlNodeList nodeList2 = xmlDoc.GetElementsByTagName("name"); // 节点集合
XmlNode elementNode = xmlDoc.GetElementById("I8"); // 单个节点
在访问节点时注意:
确保 XML 文档已经加载到XmlDocument对象中,可以使用Load()方法加载 XML 文档。
需要合理编写 XPath 表达式或指定正确的节点名称,以确保可以准确访问到目标节点。
使用 SelectSingleNode() 方法或 GetElementById() 方法时,可以对返回的单个节点
进行访问和操作;而使用 SelectNodes() 方法或 GetElementsByTagName() 方法
时,需要通过遍历 XmlNodeList 来访问每个节点。
在操作节点时,也要注意节点的类型和属性的存在与否,可以根据具体的需求来选择合
适的属性和方法进行访问。
总之,使用 XPath 表达式或节点名称来访问节点时,要确保正确理解 XML 文档的结构
和节点之间的关系,并根据需求来选择合适的访问方法和属性。
下面使用XDocument访问结点.
WebClient wc = new WebClient();
wc.Encoding = Encoding.UTF8;
string s = wc.DownloadString(@"https://sspai.com/feed");
XDocument xDoc = XDocument.Parse(s);
XElement xRoot = xDoc.Root;
IEnumerable xe = xRoot.Element("channel").Elements("item");
foreach (XElement item in xe)
{
Console.WriteLine(item.Name.ToString());
}
xe = xRoot.Descendants("item").Where(x => x.Element("pubDate").Value.ToString().Contains("Fri"));
foreach (XElement item in xe)
{
Console.WriteLine(item.Element("pubDate").Value.ToString());
}
Descendants() 方法返回的是指定节点及其子孙节点的集合,即所有后代节点。在文档
树中从当前节点开始的所有后代元素节点(包括当前节点自身)的集合。换句话说,它会递
归遍历当前节点及其下属节点,并返回所有的后代节点。
elements是当前节点的直接子节点。不包括孙结点及后面的。
XDocument xdoc = XDocument.Load("example.xml");
IEnumerable descendants = xdoc.Descendants("root")
.Elements("element")
.Where(e => e.Attribute("id")?.Value == "I8");
foreach (XElement descendant in descendants)
{
// 对满足条件的后代节点进行处理
Console.WriteLine(descendant.Name);
}
6、GetElementByID()特别说明
GetElementByID() 方法用于通过 XML 元素的 ID 属性来获取对应的元素节点。
在 XML 中,ID 属性并不是内置的,需要将某个属性声明为 ID 属性。在 HTML 中,通
常使用id属性来表示元素的ID属性;而在其他XML文档中,可以通过DTD或XML Schema定义特
定的属性为 ID 属性。
要确定或判断一个属性是否被定义为ID属性,可以查看XML文档的DTD(Document Type
Definition)或 XML Schema,以了解属性是否被定义为 ID 属性。
右击项目,添加->新建项,右上角筛选中填入xml,选中"XML文件"后命名XMLFile.xml保
存,并在项目中拖动该文件到bin\Debug下,即与程序同一目录。双击该xml编辑:
Tom
John
可以使用 GetElementById() 方法来获取具有对应 ID 的元素节点:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("example.xml");
XmlElement element = xmlDoc.GetElementById("I01");
if (element != null)
{
// 通过 ID 属性获取到了元素节点
// 可以在此对该节点进行操作或访问
string name = element.SelectSingleNode("name").InnerText;
Console.WriteLine("Name: " + name);
}
实际上,上面会报错,因为ID不是我们认为的ID,要在 XML 文档中声明一个属性为 ID
属性,可以使用 DTD(Document Type Definition)或 XML Schema 两种方式。
(1)使用 DTD 声明 ID 属性:
在 DTD 中,可以使用 ID 或 IDREF 类型来声明属性。以下是一个示例:
]>
Tom
John
上面通过在中使用ID类型,将element元素的id属性声明为ID属性。
(2)使用 XML Schema 声明 ID 属性:
在 XML Schema 中,可以使用 xsd:ID 类型来声明属性。以下是一个示例:
上面通过将id属性的type设置为xsd:ID,将element元素的id属性声明为ID属性。
简言之,通过DTD或XML Schema来说明GetElementById中的Id到底是指哪一个。
注意:加了上面的DTD和xml scheme后,要求id的内容不能是数字,必须字母开头。
即id="01"会报警示。
7、练习:读取下面xml文件
杨中科
BJ200888
先用XmlDocument,注意本办法不推荐!
string p = @"D:\OneDrive\附件\C#加强\Orders.xml";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(p);
XmlElement xmlRoot = xmlDoc.DocumentElement;
XmlElement xmlCus = (XmlElement)xmlRoot.SelectSingleNode(@"//Order/CustomerName");
Console.WriteLine($"客户名称:{xmlCus.InnerText}");
XmlElement xmlOrd = (XmlElement)xmlRoot.SelectSingleNode(@"//Order/OrderNumber");
Console.WriteLine($"订单号:{xmlOrd.InnerText}");
XmlNodeList xmlItems = xmlRoot.GetElementsByTagName("OrderItem");
foreach (XmlNode item in xmlItems)
{
XmlElement ele = (XmlElement)item;
Console.WriteLine(ele.GetAttribute("Name") + ":" + ele.GetAttribute("Count"));
}
推荐使用XDocument方式读取。
使用XDocument类读取XML文件提供了更直观的API和更好的性能,特别是当处理大型
XML文件时。它还支持LINQ查询语法,方便过滤和处理XML数据。因此,XDocument是推荐
的XML文件读取方式。
string p = @"D:\OneDrive\附件\C#加强\Orders.xml";
XDocument doc = XDocument.Load(p);
XElement root = doc.Root;
Console.WriteLine($"客户姓名:{root.Element("CustomerName").Value}");
Console.WriteLine($"订单编号:{root.Element("OrderNumber").Value}");
foreach (XElement ele in root.Descendants("OrderItem"))
{
Console.WriteLine($"名称:{ele.Attribute("Name").Value},数量:{ele.Attribute("Count").Value}");
}
用Xdocument读取下面xml
<交易码 val="1000"/>
<流水号 val="100000000000000001"/>
<金额 val="1234567890.12"/>
<付款机构 val="腾讯销售部"/>
<付款单位账号 val="12345678901234567890"/>
<收款机构 val="新浪财务部"/>
<收款单位账号 val="12345678901234567890"/>
<交易码 val="1000"/>
<流水号 val="100000000000000002"/>
<金额 val="1234567890.12"/>
<付款机构 val="1234"/>
<付款单位账号 val="12345678901234567890"/>
<收款机构 val="1234"/>
<收款单位账号 val="12345678901234567890"/>
<交易码 val="1000"/>
<流水号 val="100000000000000003"/>
<金额 val="1234567890.12"/>
<付款机构 val="1234"/>
<付款单位账号 val="12345678901234567890"/>
<收款机构 val="1234"/>
<收款单位账号 val="12345678901234567890"/>
逐个列举读取:
string p = @"D:\OneDrive\附件\C#加强\ytbank.xml";
XDocument doc = XDocument.Load(p);
XElement root = doc.Root;
foreach (XElement ele in root.Descendants("MSG"))
{
foreach (XElement ele2 in ele.Elements())
{
Console.WriteLine($"{ele2.Name}:{ele2.Attribute("val").Value}");
}
}
8、练习:追加人员信息到列表中,双击列表读取出来,然后可以修改。最后退出保存xml。
核心用list
(1)建立一个对象,方便存储一个人的信息
public class Person
{
public Person()
{
}
public Person(string name, string age, string email, string id)
{
Name = name; Age = age; Email = email; ID = id;
}
public string Name { get; set; }
public string Age { get; set; }
public string Email { get; set; }
public string ID { get; set; }
public override string ToString()
{
return Name + "," + Age + "," + Email + "," + ID;
}
}
(2)主界面代码:
public XDocument xDoc;
public XElement root;
public bool blEdit = false;
public List lists = new List();
private void button1_Click(object sender, EventArgs e)
{
if (txtName.Text == "")
{
MessageBox.Show("没有内容,请重新输入");
return;
}
Person p = new Person(txtName.Text, txtAge.Text, txtEmail.Text, txtID.Text);
if (blEdit == true)//编辑
{
lists[listBox1.SelectedIndex] = p;
listBox1.Items[listBox1.SelectedIndex] = p;
blEdit = false;//恢复默认的添加状态
}
else//添加
{
lists.Add(p);
listBox1.Items.Add(p.ToString());
}
ClearTexts();
}
private void button2_Click(object sender, EventArgs e)
{
this.Close();
}
private void listBox1_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (listBox1.SelectedIndex != -1)
{
blEdit = true;//双击时变化为编辑状态
txtName.Text = lists[listBox1.SelectedIndex].Name;
txtAge.Text = lists[listBox1.SelectedIndex].Age;
txtEmail.Text = lists[listBox1.SelectedIndex].Email;
txtID.Text = lists[listBox1.SelectedIndex].ID;
}
}
private void Form1_Load(object sender, EventArgs e)
{
if (!File.Exists("class.xml")) return;//无xml跳过
xDoc = XDocument.Load("class.xml");
XElement root = xDoc.Element("root");
foreach (XElement stu in root.Elements("stu"))
{
Person p = new Person();
p.Name = stu.Element("name").Value;
p.Age = stu.Element("age").Value;
p.Email = stu.Element("email").Value;
p.ID = stu.Element("id").Value;
lists.Add(p);
listBox1.Items.Add(p);
}
}
private void ClearTexts()
{
foreach (TextBox tb in this.Controls.OfType())
{
tb.Text = string.Empty;
}
}
private void SaveXml()
{
if (lists.Count == 0) return;//无内容退出
xDoc = new XDocument();
root = new XElement("root");
xDoc.Add(root);
for (int i = 0; i < lists.Count; i++)
{
Person p = lists[i];
XElement stu = new XElement("stu");
stu.Add(new XElement("name", p.Name));
stu.Add(new XElement("age", p.Age));
stu.Add(new XElement("email", p.Email));
stu.Add(new XElement("id", p.ID));
root.Add(stu);
}
xDoc.Save("class.xml");
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
SaveXml();
}