C# XmlDocument
最后修改于 2023 年 7 月 5 日
C# XmlDocument 教程展示了如何在 C# 中使用 XmlDocument 处理 XML。
可扩展标记语言 (XML) 是一种标记语言,它定义了一组规则,用于以人类可读和机器可读的格式编码文档。 XML 通常用于应用程序配置、数据存储和交换。
XML 类似于 HTML,但没有预定义的标签;我们可以设计自己的标签。
XmlDocument
XmlDocument 表示一个 XML 文档。它可用于加载、修改、验证和导航 XML 文档。
XmlDocument 类是 XML 文档的内存表示。 它实现了 W3C XML 文档对象模型 (DOM)。
文档对象模型 (DOM) 是用于 HTML 和 XML 文档的与语言无关的编程接口。 它表示一个页面。 通过 DOM 接口,程序可以更改文档结构、样式和内容。 DOM 将文档表示为节点和对象。
XmlElement 是 XmlDocument 中的一个常见节点。
XPath (XML Path Language) 是一种查询语言,用于从 XML 文档中选择节点。 它还可以用于计算 XML 文档内容中的值。
在示例中,我们使用以下文件
<?xml version="1.0" encoding="UTF-8"?>
<words>
<word>falcon</word>
<word>sky</word>
<word>bottom</word>
<word>cup</word>
<word>book</word>
<word>rock</word>
<word>sand</word>
<word>river</word>
</words>
这是 words.xml 文件。
<?xml version="1.0" encoding="UTF-8"?>
<users>
<user id="1">
<name>John Doe</name>
<occupation>gardener</occupation>
</user>
<user id="2">
<name>Jane Doe</name>
<occupation>teacher</occupation>
</user>
<user id="3">
<name>Roger Roe</name>
<occupation>driver</occupation>
</user>
<user id="4">
<name>Lucia Smith</name>
<occupation>shopkeeper</occupation>
</user>
</users>
这是 users.xml 文件。
<?xml version="1.0" encoding="UTF-8"?>
<continents>
<europe>
<slovakia>
<capital>Bratislava</capital>
<population>421000</population>
</slovakia>
<hungary>
<capital>Budapest</capital>
<population>1759000</population>
</hungary>
<poland>
<capital>Warsaw</capital>
<population>1735000</population>
</poland>
</europe>
<asia>
<china>
<capital>Beijing</capital>
<population>21700000</population>
</china>
<vietnam>
<capital>Hanoi</capital>
<population>7500000</population>
</vietnam>
</asia>
</continents>
这是 continents.xml 文件。
C# XmlDocument DocumentElement
DocumentElement 返回文档的根 XmlElement。
using System.Xml;
var xmlData = @"<?xml version=""1.0"" encoding=""UTF-8""?>
<words>
<word>falcon</word>
<word>sky</word>
<word>bottom</word>
<word>cup</word>
<word>book</word>
<word>rock</word>
<word>sand</word>
<word>river</word>
</words>
";
var doc = new XmlDocument();
doc.LoadXml(xmlData);
Console.WriteLine(doc.DocumentElement?.Name);
Console.WriteLine(doc.DocumentElement?.FirstChild?.InnerText);
Console.WriteLine(doc.DocumentElement?.LastChild?.InnerText);
Console.WriteLine(doc.DocumentElement?.OuterXml);
Console.WriteLine(doc.DocumentElement?.InnerXml);
我们将 XML 数据加载到 XmlDocument 中,并获取文档的根元素;我们调用根节点的属性。
var xmlData = @"<?xml version=""1.0"" encoding=""UTF-8""?> <words> <word>falcon</word> <word>sky</word> <word>bottom</word> <word>cup</word> <word>book</word> <word>rock</word> <word>sand</word> <word>river</word> </words> ";
我们有作为多行字符串的 XML 数据。
var doc = new XmlDocument(); doc.LoadXml(xmlData);
我们创建一个 XmlDocument 并使用 LoadXml 方法加载 XML 数据。
Console.WriteLine(doc.DocumentElement?.Name);
Name 属性返回节点的名称。
Console.WriteLine(doc.DocumentElement?.FirstChild?.InnerText); Console.WriteLine(doc.DocumentElement?.LastChild?.InnerText);
我们使用 FirstChild 和 LastChild 获取根节点的第一个和最后一个子节点。 InnerText 属性返回节点及其所有子节点的值。
Console.WriteLine(doc.DocumentElement?.OuterXml);
OuterXml 属性获取包含此节点及其所有子节点的标记。
Console.WriteLine(doc.DocumentElement?.InnerXml);
InnerXml 属性获取或设置仅表示此节点的子节点的标记。
$ dotnet run words falcon river <words><word>falcon</word><word>sky</word><word>bottom</word><word>cup</word>... <word>falcon</word><word>sky</word><word>bottom</word><word>cup</word>...
C# XmlNode.RemoveChild
XmlNode.RemoveChild 方法删除指定的子节点。
using System.Xml;
var xmlFile = "/home/janbodnar/Documents/users.xml";
var doc = new XmlDocument();
doc.Load(xmlFile);
XmlElement root = doc.DocumentElement;
XmlNode userNode = root?.LastChild;
if (userNode != null)
{
root.RemoveChild(userNode);
}
var xmlFile2 = "/home/janbodnar/Documents/users2.xml";
doc.Save(xmlFile2);
在该示例中,我们删除文档中的最后一个子节点。 请注意,该方法不修改原始文件; 它修改文档的内存表示,并将修改后的文档保存到新文件中。
C# XmlDocument.CreateElement
XmlDocument.CreateElement 方法创建具有指定名称的元素。
using System.Xml;
var xmlFile = "/home/janbodnar/Documents/words.xml";
var doc = new XmlDocument();
doc.Load(xmlFile);
XmlElement root = doc.DocumentElement;
XmlElement e1 = doc.CreateElement("word");
e1.InnerText = "eagle";
root?.InsertAfter(e1, root.LastChild);
XmlElement e2 = doc.CreateElement("word");
e2.InnerText = "cheetah";
root?.InsertBefore(e2, root.FirstChild);
var xmlFile2 = "/home/janbodnar/Documents/words2.xml";
doc.Save(xmlFile2);
在示例中,我们创建两个新元素。
XmlElement e1 = doc.CreateElement("word");
e1.InnerText = "eagle";
使用 CreateElement 创建一个名为 word 的新元素。 它的文本使用 InnerText 属性设置。
root?.InsertAfter(e1, root.LastChild);
使用 InsertAfter 将新创建的元素插入到最后一个子节点之后。
XmlElement e2 = doc.CreateElement("word");
e2.InnerText = "cheetah";
root?.InsertBefore(e2, root.FirstChild);
使用 InsertBefore 将第二个元素插入到第一个元素之前。
C# XmlDocument 创建新文档
在以下示例中,我们创建一个新的 XML 文档。
using System.Xml;
var users = new Dictionary<long, User>();
users.Add(1, new User(1L, "John Doe", "gardener"));
users.Add(2, new User(2L, "Jane Doe", "teacher"));
users.Add(3, new User(3L, "Roger Roe", "driver"));
users.Add(4, new User(4L, "Lucia Smith", "shopkeeper"));
var doc = new XmlDocument();
XmlDeclaration xmlDeclaration = doc.CreateXmlDeclaration("1.0",
"UTF-8", string.Empty);
doc.AppendChild(xmlDeclaration);
XmlElement usersNode = doc.CreateElement("users");
doc.AppendChild(usersNode);
foreach (var (_, value) in users)
{
XmlElement userEl = doc.CreateElement("user");
usersNode.AppendChild(userEl);
XmlAttribute e = doc.CreateAttribute("id");
e.Value = value.Id.ToString();
userEl.Attributes.Append(e);
XmlElement e2 = doc.CreateElement("name");
e2.InnerText = value.Name;
userEl.AppendChild(e2);
XmlElement e3 = doc.CreateElement("occupation");
e3.InnerText = value.Occupation;
userEl.AppendChild(e3);
}
doc.Save(Console.Out);
internal record User(long Id, string Name, string Occupation);
从用户对象的字典创建一个新的 XML 文档。
var users = new Dictionary<long, User>(); users.Add(1, new User(1L, "John Doe", "gardener")); users.Add(2, new User(2L, "Jane Doe", "teacher")); users.Add(3, new User(3L, "Roger Roe", "driver")); users.Add(4, new User(4L, "Lucia Smith", "shopkeeper"));
我们有一个用户字典。
var doc = new XmlDocument();
XmlDeclaration xmlDeclaration = doc.CreateXmlDeclaration("1.0",
"UTF-8", string.Empty);
创建一个 XmlDocument 和一个 XmlDeclaration。
doc.AppendChild(xmlDeclaration);
使用 AppendChild 将 XmlDeclaration 附加到文档。 声明标签是文档中的第一个标签。
XmlElement usersNode = doc.CreateElement("users");
doc.AppendChild(usersNode);
我们创建文档的根节点。
foreach (var (_, value) in users)
{
XmlElement userEl = doc.CreateElement("user");
usersNode.AppendChild(userEl);
XmlAttribute e = doc.CreateAttribute("id");
e.Value = value.Id.ToString();
userEl.Attributes.Append(e);
XmlElement e2 = doc.CreateElement("name");
e2.InnerText = value.Name;
userEl.AppendChild(e2);
XmlElement e3 = doc.CreateElement("occupation");
e3.InnerText = value.Occupation;
userEl.AppendChild(e3);
}
我们遍历字典,并为每个用户对象创建必要的元素和一个属性。
doc.Save(Console.Out);
这次我们将 XML 数据输出到控制台。
$ dotnet run
<?xml version="1.0" encoding="utf-8"?>
<users>
<user id="1">
<name>John Doe</name>
<occupation>gardener</occupation>
</user>
<user id="2">
<name>Jane Doe</name>
<occupation>teacher</occupation>
</user>
<user id="3">
<name>Roger Roe</name>
<occupation>driver</occupation>
</user>
<user id="4">
<name>Lucia Smith</name>
<occupation>shopkeeper</occupation>
</user>
</users>
C# XmlNode.ChildNodes
XmlNode.ChildNodes 返回给定节点的所有子节点。 该属性返回 XmlNodeList,它表示节点的有序集合。
using System.Xml;
var xmlFile = "/home/janbodnar/Documents/words.xml";
var doc = new XmlDocument();
doc.Load(xmlFile);
XmlElement root = doc.DocumentElement;
XmlNodeList childNodes = root?.ChildNodes;
if (childNodes == null)
{
Console.WriteLine("no nodes found");
Environment.Exit(1);
}
foreach (XmlNode node in childNodes)
{
Console.WriteLine(node.InnerText);
}
在示例中,我们获取根节点的所有子节点并打印它们的内部文本内容。
$ dotnet run falcon sky bottom cup book rock sand river
C# XmlNodeList.Count
XmlNodeList.Count 属性获取 XmlNodeList 中的节点数。
using System.Xml;
var xmlFile = "/home/janbodnar/Documents/words.xml";
var doc = new XmlDocument();
doc.Load(xmlFile);
XmlElement root = doc.DocumentElement;
int? n = root?.ChildNodes.Count;
Console.WriteLine($"There are {n} elements");
我们获取根节点内部的元素数量。
$ dotnet run There are 8 elements
C# XmlNode.SelectSingleNode
XmlNode.SelectSingleNode 选择与 XPath 表达式匹配的第一个 XmlNode。
using System.Xml;
var xmlFile = "/home/janbodnar/Documents/users.xml";
var doc = new XmlDocument();
doc.Load(xmlFile);
int id = 2;
XmlNode node = doc.SelectSingleNode($"/users/user[@id='{id}']");
if (node == null)
{
Console.WriteLine("node not found");
Environment.Exit(1);
}
var name = node.ChildNodes[0]?.InnerText;
var occupation = node.ChildNodes[1]?.InnerText;
var uid = node.Attributes?["id"]?.Value;
Console.WriteLine($"Id: {uid}");
Console.WriteLine($"Name: {name}");
Console.WriteLine($"Occupation: {occupation}");
从 words.xml 文件中,我们选择 Id 属性为 6 的 word 节点。
XmlNode node = doc.SelectSingleNode($"/users/user[@id='{id}']");
使用 SelectSingleNode 选择单个节点; /users/user[@id='{id}'] 是获取所需节点的查询表达式。
var name = node.ChildNodes[0]?.InnerText; var occupation = node.ChildNodes[1]?.InnerText; var uid = node.Attributes?["id"]?.Value;
我们获取节点的文本和属性。
$ dotnet run Id: 2 Name: Jane Doe Occupation: teacher
C# XmlNode.SelectNodes
XmlNode.SelectNodes 选择与 XPath 表达式匹配的节点列表。
using System.Xml;
var xmlFile = "/home/janbodnar/Documents/users.xml";
var doc = new XmlDocument();
doc.Load(xmlFile);
XmlNodeList nodes = doc.SelectNodes("/users/user");
var users = new List<User>();
if (nodes == null)
{
Console.WriteLine("No users found");
Environment.Exit(1);
}
foreach (XmlNode node in nodes)
{
long id = long.Parse(node.Attributes?.GetNamedItem("id")?.Value!);
string name = node.ChildNodes[0]?.InnerText;
string occupation = node.ChildNodes[1]?.InnerText;
var user = new User(id, name, occupation);
users.Add(user);
}
users.ForEach(Console.WriteLine);
record User(long Id, string Name, string Occupation);
在示例中,我们从 users.xml 文件中选择所有用户。
XmlNodeList nodes = doc.SelectNodes("/users/user");
/users/user 是获取所有用户的路径。 SelectNodes 方法返回一个 XmlNodeList。
foreach (XmlNode node in nodes)
{
long id = long.Parse(node.Attributes?.GetNamedItem("id")?.Value!);
string name = node.ChildNodes[0]?.InnerText;
string occupation = node.ChildNodes[1]?.InnerText;
var user = new User(id, name, occupation);
users.Add(user);
}
我们遍历 XmlNodeList 并从检索到的数据创建一个 User 对象。
$ dotnet run
User { Id = 1, Name = John Doe, Occupation = gardener }
User { Id = 2, Name = Jane Doe, Occupation = teacher }
User { Id = 3, Name = Roger Roe, Occupation = driver }
User { Id = 4, Name = Lucia Smith, Occupation = shopkeeper }
C# XmlElement.GetElementsByTagName
XmlElement.GetElementsByTagName 返回一个 XmlNodeList,其中包含与指定标签名称匹配的所有后代元素的列表。
using System.Xml;
var xmlFile = "/home/janbodnar/Documents/continents.xml";
XmlDocument doc = new XmlDocument();
doc.Load(xmlFile);
XmlNodeList nodes = doc.GetElementsByTagName("capital");
Console.WriteLine("All capitals:");
foreach (XmlNode node in nodes)
{
var text = node.InnerText;
Console.WriteLine(text);
}
我们遍历 continents.xml 文件中的 capital 标签。
$ dotnet run All capitals: Bratislava Budapest Warsaw Beijing Hanoi
C# XmlDocument.CreateNavigator
XmlDocument.CreateNavigator 创建一个 XPathNavigator 对象,用于导航文档。 XPathNodeIterator 提供了对选定节点集的迭代器。
using System.Xml;
using System.Xml.XPath;
var xmlFile = "/home/janbodnar/Documents/users.xml";
var doc = new XmlDocument();
doc.Load(xmlFile);
XPathNavigator rootNav = doc.CreateNavigator();
XPathNodeIterator it = rootNav?.Select("descendant::users/user");
if (it == null)
{
Console.WriteLine("no users found");
Environment.Exit(1);
}
while (it.MoveNext())
{
XPathNavigator nav = it.Current;
var uId = nav?.GetAttribute("id", string.Empty);
Console.WriteLine(uId);
XPathNodeIterator nodeIt = nav?.SelectChildren(XPathNodeType.Element);
if (nodeIt != null)
{
foreach (var e in nodeIt)
{
Console.WriteLine(e);
}
}
Console.WriteLine("--------------------");
}
我们使用 XPathNavigator 和 XPathNodeIterator 来遍历 users.xml 文件中的用户。
XPathNavigator rootNav = doc.CreateNavigator();
XPathNodeIterator it = rootNav?.Select("descendant::users/user");
...
while (it.MoveNext())
首先,我们遍历 user 标签。
XPathNavigator nav = it.Current;
var uId = nav?.GetAttribute("id", string.Empty);
Console.WriteLine(uId);
XPathNodeIterator nodeIt = nav?.SelectChildren(XPathNodeType.Element);
if (nodeIt != null)
{
foreach (var e in nodeIt)
{
Console.WriteLine(e);
}
}
然后我们遍历每个 user 标签的元素。
$ dotnet run 1 John Doe gardener -------------------- 2 Jane Doe teacher -------------------- 3 Roger Roe driver -------------------- 4 Lucia Smith shopkeeper --------------------
C# XmlDocument 递归循环
在以下示例中,我们递归循环 users.xml 文件的标签。
using System.Xml;
var xmlFile = "/home/janbodnar/Documents/users.xml";
var doc = new XmlDocument();
doc.Load(xmlFile);
traverse(doc.DocumentElement);
void traverse(XmlNode node)
{
if (node is XmlElement)
{
if (node.Name == "users")
{
Console.WriteLine(node.Name);
}
else if (node.Name == "user")
{
Console.WriteLine("--------------------");
Console.WriteLine(node.Name);
}
else
{
Console.WriteLine($" {node.Name}");
}
if (node.HasChildNodes)
{
traverse(node.FirstChild);
}
if (node.NextSibling != null)
{
traverse(node.NextSibling);
}
}
else if (node is XmlText)
{
var text = ((XmlText) node).Value;
Console.WriteLine($" {text}");
}
}
我们使用递归算法解析 users.xml。
traverse(doc.DocumentElement);
我们首先将根元素传递给 traverse 方法。
if (node is XmlElement)
{
...
}
else if (node is XmlText)
{
var text = ((XmlText) node).Value;
Console.WriteLine($" {text}");
}
我们检查节点是否为 XmlElement 或 XmlText。 在后一种情况下,我们输出节点的文本内容。
if (node.HasChildNodes)
{
traverse(node.FirstChild);
}
如果节点有子节点,我们递归调用 traverse 方法,并将其第一个子节点传递给它。
if (node.NextSibling != null)
{
traverse(node.NextSibling);
}
如果存在,我们递归地转到下一个同级节点。
$ dotnet run
users
--------------------
user
name
John Doe
occupation
gardener
--------------------
user
name
Jane Doe
occupation
teacher
--------------------
user
name
Roger Roe
occupation
driver
--------------------
user
name
Lucia Smith
occupation
shopkeeper
在本文中,我们使用 XmlDocument 处理了 C# 中的 XML 数据。
C# XmlDocument 读取货币汇率
在以下示例中,我们从欧洲中央银行读取数据。
using System.Xml;
var xmlRes = "http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml";
var doc = new XmlDocument();
doc.Load(xmlRes);
XmlNodeList nodes = doc.DocumentElement?.ChildNodes[2]?.ChildNodes[0]?.ChildNodes;
foreach (XmlNode xmlNode in nodes!)
{
if (xmlNode.Attributes == null) continue;
var cur = xmlNode.Attributes["currency"]?.Value;
var rate = xmlNode.Attributes["rate"]?.Value;
Console.WriteLine($"{cur}: {rate}");
}
我们获取欧元的货币汇率。
$ dotnet run USD: 1.1904 JPY: 130.20 BGN: 1.9558 CZK: 26.031 DKK: 7.4369 GBP: 0.86518 HUF: 356.43 PLN: 4.5246 RON: 4.9203 SEK: 10.1975 CHF: 1.0998 ISK: 151.90 NOK: 10.0940 HRK: 7.5673 RUB: 91.9588 ...
C# XmlDocument 验证
具有正确语法的 XML 文档被称为格式良好。 验证是检查 XML 文档以确认其既格式良好又有效的过程。 有效文档遵守特定 DTD 或 XML 架构规定的规则。
XML 架构或 XML 架构定义 (XSD) 是对 XML 文档的结构和内容的正式描述。 它用于验证 XML 文档。
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="users" type="usersType"/>
<xs:complexType name="userType">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="occupation" type="xs:string"/>
</xs:sequence>
<xs:attribute type="xs:string" name="id"/>
</xs:complexType>
<xs:complexType name="usersType">
<xs:sequence>
<xs:element name="user" type="userType" maxOccurs="unbounded" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
我们有 users.xml 文件的架构定义。
using System.Xml;
using System.Xml.Schema;
var xmlFile = "/home/janbodnar/Documents/users.xml";
var xmlSchema = "/home/janbodnar/Documents/users.xsd";
try
{
var settings = new XmlReaderSettings();
settings.Schemas.Add(null, xmlSchema);
settings.ValidationType = ValidationType.Schema;
settings.ValidationEventHandler += ValidationEventHandler;
XmlReader reader = XmlReader.Create(xmlFile, settings);
var doc = new XmlDocument();
doc.Load(reader);
Console.WriteLine("validation passed");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
void ValidationEventHandler(object sender, ValidationEventArgs e)
{
Console.WriteLine($"Document validation error: {e.Message}");
Environment.Exit(1);
}
我们根据 users.xsd 架构验证 users.xml 文档。
C# XmlDocument 事件
有一些事件处理程序可以对节点被更改、插入或删除做出反应。
using System.Xml;
var xmlFile = "/home/janbodnar/Documents/words.xml";
var doc = new XmlDocument();
doc.Load(xmlFile);
XmlElement root = doc.DocumentElement;
doc.NodeChanged += MyNodeChangedEvent;
doc.NodeInserted += MyNodeInsertedEvent;
doc.NodeRemoved += MyNodeRemovedEvent;
XmlNode node = root?.LastChild;
root?.RemoveChild(node!);
XmlElement e1 = doc.CreateElement("word");
e1.InnerText = "eagle";
root?.AppendChild(e1);
XmlNode n1 = doc.SelectSingleNode("//words/word[text()='bottom']");
Console.WriteLine(n1?.InnerText);
if (n1 != null) n1.InnerText = "star";
doc.Save("/home/janbodnar/Documents/words3.xml");
void MyNodeChangedEvent(Object src, XmlNodeChangedEventArgs args)
{
Console.WriteLine($"Node Changed Event Fired for node {args.Node?.Name}");
Console.WriteLine(args.Node?.Value);
}
void MyNodeInsertedEvent(Object src, XmlNodeChangedEventArgs args)
{
Console.WriteLine($"Node Inserted Event Fired for node {args.Node?.Name}");
Console.WriteLine(args.Node?.Value);
}
void MyNodeRemovedEvent(Object src, XmlNodeChangedEventArgs args)
{
Console.WriteLine($"Node Removed Event Fired for node {args.Node?.Name}");
Console.WriteLine(args.Node?.Value);
}
在示例中,我们创建了三个事件处理程序。
doc.NodeChanged += MyNodeChangedEvent; doc.NodeInserted += MyNodeInsertedEvent; doc.NodeRemoved += MyNodeRemovedEvent;
我们为节点被更改、插入和删除插入事件处理程序。
XmlNode node = root?.LastChild; root?.RemoveChild(node!);
我们删除最后一个子节点。 这会触发 MyNodeRemovedEvent。
XmlElement e1 = doc.CreateElement("word");
e1.InnerText = "eagle";
root?.AppendChild(e1);
我们创建一个新节点。 这会触发两段 MyNodeInsertedEvent:一段用于插入文本,一段用于插入节点。
XmlNode n1 = doc.SelectSingleNode("//words/word[text()='bottom']");
Console.WriteLine(n1?.InnerText);
if (n1 != null) n1.InnerText = "star";
我们修改节点的内容。 这会触发 MyNodeChangedEvent。
$ dotnet run Node Removed Event Fired for node word Node Inserted Event Fired for node #text eagle Node Inserted Event Fired for node word bottom Node Changed Event Fired for node #text star
来源
在本文中,我们使用 XmlDocument 处理了 C# 中的 XML 数据。
作者
列出所有 C# 教程。