如何在C#中处理XML

90

在C# 2.0中处理XML文档和XSD的最佳方法是什么?

使用哪些类等。解析和创建XML文档等的最佳实践是什么。

编辑:欢迎提供.Net 3.5建议。


1
对于那些试图寻找更可行解决方案的人,请忽略此内容。这是一个旧的.NET库。改用XDocument,你就不用挖眼睛来纾解沮丧了。 - AER
12个回答

187

C# 2.0中阅读和写入的主要方式是通过XmlDocument类完成的。您可以通过它接受的XmlReader直接将大多数设置加载到XmlDocument中。

直接加载XML

XmlDocument document = new XmlDocument();
document.LoadXml("<People><Person Name='Nick' /><Person Name='Joe' /></People>");

从文件中加载XML

XmlDocument document = new XmlDocument();
document.Load(@"C:\Path\To\xmldoc.xml");
// Or using an XmlReader/XmlTextReader
XmlReader reader = XmlReader.Create(@"C:\Path\To\xmldoc.xml");
document.Load(reader);

我发现使用XPath是阅读XML文档的最简单/快速的方法。

使用XPath阅读XML文档(使用XmlDocument允许我们进行编辑)

XmlDocument document = new XmlDocument();
document.LoadXml("<People><Person Name='Nick' /><Person Name='Joe' /></People>");

// Select a single node
XmlNode node = document.SelectSingleNode("/People/Person[@Name = 'Nick']");

// Select a list of nodes
XmlNodeList nodes = document.SelectNodes("/People/Person");

如果您需要使用XSD文档来验证XML文档,可以使用此方法。

针对XSD模式验证XML文档

XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidateType = ValidationType.Schema;
settings.Schemas.Add("", pathToXsd); // targetNamespace, pathToXsd

XmlReader reader = XmlReader.Create(pathToXml, settings);
XmlDocument document = new XmlDocument();

try {
    document.Load(reader);
} catch (XmlSchemaValidationException ex) { Trace.WriteLine(ex.Message); }

每个节点上验证XML与XSD(更新1)

XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidateType = ValidationType.Schema;
settings.Schemas.Add("", pathToXsd); // targetNamespace, pathToXsd
settings.ValidationEventHandler += new ValidationEventHandler(settings_ValidationEventHandler);

XmlReader reader = XmlReader.Create(pathToXml, settings);
while (reader.Read()) { }

private void settings_ValidationEventHandler(object sender, ValidationEventArgs args)
{
    // e.Message, e.Severity (warning, error), e.Error
    // or you can access the reader if you have access to it
    // reader.LineNumber, reader.LinePosition.. etc
}

手动编写XML文档

XmlWriter writer = XmlWriter.Create(pathToOutput);
writer.WriteStartDocument();
writer.WriteStartElement("People");

writer.WriteStartElement("Person");
writer.WriteAttributeString("Name", "Nick");
writer.WriteEndElement();

writer.WriteStartElement("Person");
writer.WriteStartAttribute("Name");
writer.WriteValue("Nick");
writer.WriteEndAttribute();
writer.WriteEndElement();

writer.WriteEndElement();
writer.WriteEndDocument();

writer.Flush();

(更新1)

在 .NET 3.5 中,您可以使用 XDocument 执行类似的任务。不同之处在于,您有优势执行 Linq 查询来选择您需要的确切数据。通过对象初始化程序的添加,您甚至可以创建一个查询,该查询本身返回您自己定义的对象。

    XDocument doc = XDocument.Load(pathToXml);
    List<Person> people = (from xnode in doc.Element("People").Elements("Person")
                       select new Person
                       {
                           Name = xnode.Attribute("Name").Value
                       }).ToList();

(更新2)

在.NET 3.5中创建XML的一种不错的方法是使用XDocument,具体代码如下。这样可以使代码的编写方式与期望的输出结果类似。

XDocument doc =
        new XDocument(
              new XDeclaration("1.0", Encoding.UTF8.HeaderName, String.Empty),
              new XComment("Xml Document"),
              new XElement("catalog",
                    new XElement("book", new XAttribute("id", "bk001"),
                          new XElement("title", "Book Title")
                    )
              )
        );
创建
<!--Xml Document-->
<catalog>
  <book id="bk001">
    <title>Book Title</title>
  </book>
</catalog>

如果以上方法都失败了,您可以查看这篇MSDN文章,里面有我在这里讨论过的许多示例以及更多内容。 http://msdn.microsoft.com/en-us/library/aa468556.aspx


3
在最后一个例子中,您可能希望指出您正在使用XDocument,因为XDocument与XmlDocument非常不同。 - Aaron Powell
2
更正一下,没有C# 3.5这个版本;你指的是.NET 3.5和C# 3.0。 - Marc Gravell
哦,而且“即时”[对象初始化程序]在C# 3.0和XmlDocument中工作方式基本相同 - 尽管仍然是一个很好的回答(+1) - Marc Gravell
这个答案非常精准地回答了我需要快速入门的“如何做”的问题。谢谢! - Eric Dand
在 .Net 5 和 .net framework 4.7.2 及更高版本中,XmlReaderSettings 中的 ValidateType 现在被称为 "ValidationType"。 - Daniel
显示剩余2条评论

32

这取决于文件大小;对于小到中等大小的xml,像XmlDocument(任何C#/.NET版本)或XDocument(.NET 3.5/C# 3.0)这样的DOM是显而易见的获胜者。如果要使用xsd,则可以使用XmlReader加载xml,并且XmlReader接受(用于CreateXmlReaderSettings。XmlReaderSettings对象具有Schemas属性,可用于执行xsd(或dtd)验证。

对于编写xml,相同的事情适用,需要注意的是,使用LINQ-to-XML(XDocument)比旧的XmlDocument更容易布局内容。

但是,对于大型xml,DOM可能会占用太多内存,在这种情况下,您可能需要直接使用XmlReader/XmlWriter。

最后,如果要操作XML,您可能希望使用XslCompiledTransform(一个XSLT层)。

与XML一起工作的替代方法是使用对象模型;您可以使用xsd.exe创建表示符合XSD的模型的类,并将XML 加载为对象,使用面向对象的方式进行操作,然后再次序列化这些对象;您可以使用XmlSerializer来完成此操作。


操作(添加/删除元素)一个大型的XML文档(40k行),最好的方法是什么?我之前使用过LINQ-to-XML。 - Neyoh

12

nyxtom的回答非常好。我想补充一些内容:

如果您只需要对XML文档进行只读访问,则XPathDocumentXmlDocument更轻量级。

使用XPathDocument的缺点是您无法使用熟悉的XmlNodeSelectNodesSelectSingleNode方法。相反,您必须使用IXPathNavigable提供的工具:使用CreateNavigator创建XPathNavigator,并使用XPathNavigator创建XPathNodeIterator以遍历通过XPath找到的节点列表。这通常需要比使用XmlDocument方法多几行代码。

但是:XmlDocumentXmlNode类实现了IXPathNavigable,因此您编写的任何使用那些方法在XPathDocument上的代码也会在XmlDocument上工作。如果您习惯于针对IXPathNavigable编写代码,则该方法可以对任何对象起作用。(这就是为什么在方法签名中使用XmlNodeXmlDocument被FxCop标记的原因。)

遗憾的是,XDocumentXElement(以及XNodeXObject)不实现IXPathNavigable

nyxtom的回答中没有提到的另一件事是XmlReader。通常,您使用XmlReader避免在开始处理之前将XML流解析为对象模型的开销。相反,您使用XmlReader逐个XML节点地处理输入流。这本质上是.NET对SAX的回答。它可以让您编写非常快速的代码来处理非常大的XML文档。

< p > < code > XmlReader 还提供了处理 XML 文档片段的最简单方法,例如 SQL Server 的 FOR XML RAW 选项返回的不包括元素的 XML 元素流。

使用 XmlReader 编写的代码通常与其读取的 XML 格式非常紧密耦合。使用 XPath 允许您的代码与 XML 相比更加松散耦合,这就是为什么通常使用它是正确的答案。但是当您需要使用 XmlReader 时,确实需要它。


3
请注意,有一个扩展方法XPathNavigator CreateNavigator(this XNode node),可以从XNode(其中包括派生类XDocument)创建一个XPathNavigator - Dave

5

首先,了解新的 XDocumentXElement 类,因为它们是对以前的 XmlDocument 家族的改进。

  1. 它们可以使用 LINQ 进行操作
  2. 它们更快且更轻量级

然而,你可能仍然需要使用旧的类来处理遗留代码 - 特别是之前生成的代理。在这种情况下,你需要熟悉一些用于在这些 XML 处理类之间进行互操作的模式。

我认为你的问题非常广泛,需要太多答案才能给出详细解释,但这是我想到的第一个通用答案,可以作为一个起点。


我同意它们(XDocument等)很棒,但OP问的是C# 2.0的问题。 - Marc Gravell

3

2

Writing XML with the XmlDocument class

//itemValues is collection of items in Key value pair format
//fileName i name of XML file which to creatd or modified with content
    private void WriteInXMLFile(System.Collections.Generic.Dictionary<string, object> itemValues, string fileName)
    {
        string filePath = "C:\\\\tempXML\\" + fileName + ".xml";
        try
        {

            if (System.IO.File.Exists(filePath))
            {
                XmlDocument doc = new XmlDocument();
                doc.Load(filePath);                   

                XmlNode rootNode = doc.SelectSingleNode("Documents");

                XmlNode pageNode = doc.CreateElement("Document");
                rootNode.AppendChild(pageNode);


                foreach (string key in itemValues.Keys)
                {

                    XmlNode attrNode = doc.CreateElement(key);
                    attrNode.InnerText = Convert.ToString(itemValues[key]);
                    pageNode.AppendChild(attrNode);
                    //doc.DocumentElement.AppendChild(attrNode);

                }
                doc.DocumentElement.AppendChild(pageNode);
                doc.Save(filePath);
            }
            else
            {
                XmlDocument doc = new XmlDocument();
                using(System.IO.FileStream fs = System.IO.File.Create(filePath))
                {
                    //Do nothing
                }

                XmlNode rootNode = doc.CreateElement("Documents");
                doc.AppendChild(rootNode);
                doc.Save(filePath);

                doc.Load(filePath);

                XmlNode pageNode = doc.CreateElement("Document");
                rootNode.AppendChild(pageNode);

                foreach (string key in itemValues.Keys)
                {                          
                    XmlNode attrNode = doc.CreateElement(key);                           
                    attrNode.InnerText = Convert.ToString(itemValues[key]);
                    pageNode.AppendChild(attrNode);
                    //doc.DocumentElement.AppendChild(attrNode);

                }
                doc.DocumentElement.AppendChild(pageNode);

                doc.Save(filePath);

            }
        }
        catch (Exception ex)
        {

        }

    }

OutPut look like below
<Dcouments>
    <Document>
        <DocID>01<DocID>
        <PageName>121<PageName>
        <Author>Mr. ABC<Author>
    <Dcoument>
    <Document>
        <DocID>02<DocID>
        <PageName>122<PageName>
        <Author>Mr. PQR<Author>
    <Dcoument>
</Dcouments>

2
如果您正在使用.NET 3.5并且不害怕实验性代码,可以查看LINQ to XSD (http://blogs.msdn.com/xmlteam/archive/2008/02/21/linq-to-xsd-alpha-0-2.aspx),它将从XSD生成.NET类(包括XSD中的内置规则)。
它还具有直接写入文件和从文件读取的功能,确保符合XSD规则。
我强烈建议为您使用的任何XML文档拥有一个XSD:
  • 允许您强制执行XML中的规则
  • 允许其他人查看XML的结构
  • 可用于验证XML
我发现Liquid XML Studio是生成XSD的好工具,并且是免费的!

1

作为一名C#程序员,我个人的看法是,在C#中处理XML的最佳方式是将该部分代码委托给VB .NET项目。在.NET 3.5中,VB .NET具有XML文本,使得处理XML更加直观。例如,请参见此处:

Visual Basic中LINQ to XML概述

(请确保将页面设置为显示VB代码,而不是C#代码。)

我会用C#编写项目的其余部分,但是在引用的VB项目中处理XML。


仅为了XML字面量而转换到VB并不值得。XML仅处理文字。如果将XML作为参数传递,则XML字面量支持并不能带来太多好处。相反,VB.NET的旧语法会破坏C#愉快的编程体验。 - Gqqnbig

1
如果您在设计器中创建了一个类型化的数据集,那么您将自动获得一个 xsd 文件和一个强类型对象,并且可以使用一行代码加载和保存 XML。

我在使用DataSet方面取得了很大的成功。它们也非常友好地与数据库配合。 - User1

0

nyxtom,

在示例1中,"doc"和"xdoc"不应该匹配吗?

XDocument **doc** = XDocument.Load(pathToXml);
List<Person> people = (from xnode in **xdoc**.Element("People").Elements("Person")
                   select new Person
                   {
                       Name = xnode.Attribute("Name").Value
                   }).ToList();

我已经提交了一个编辑请求,希望能够通过你所提到的答案,但是这应该是一条评论,而不是一个答案。 - David Thompson
谢谢David。同意,当时它不允许我评论。不确定为什么。 - mokumaxCraig

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接