鉴于大家对ASP.net十分关注,我们编辑小组在此为大家搜集整理了“用Visual C++.NET实现XML解析”一文,供大家参考学习
对于多数开发者来说,Web服务已经成为某种固定不变的东西了,因为他们把Internet看作是促进应用程序应用的最好途径。一些Web服务(例如Amazon.com Web服务)是公共的,并且很容易被要求把这种服务包含在应用程序中。其它一些Web服务是私有的,并且需要访问的特定知识。无论涉及哪种Web服务,他们都依赖XML传递数据。因此,在提到Web服务的时候,XML解析是开发者想到的第一件事。
XML解析的其它用途
XML解析并不仅仅局限于Web服务,例如.NET应用程序就能使用XML作为配置信息的存储方法。你需要做的事情就是拥有一个.MANIFEST文件,它的名称必须与应用程序的名称相同,CLR(通用语言运行时)会自动查阅这个文件找到配置信息。
例如,列表1显示了添加给一个.MANIFEST文件的XML。这个清单告诉CLR使用Windows XP主题信息绘制标准控件(该配置文件不会影响自己绘制的控件)。注意该文件的版本号可能改变,可以在系统的\WINDOWS\WinSxS文件夹下查看当前的版本号。这个并行库的文件夹的名称可能类似x86_Microsoft.Windows.Common-Controls_6595b64144ccf1df_6.0.0.0_x-ww_1382d70a,列表1中显示的processorArchitecture值最先出现,接着出现名称(name)值,然后出现publicKeyToken值,最后出现版本(version)值。
列表1..NET.MANIFEST文件依赖XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32"
name="ShowMessage"
version="1.0.0.0"
processorArchitecture="x86"
/>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="X86"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</assembly>
你也可以建立其它类型的配置文件,你的应用程序可以在启动过程中分析它。这些文件可以包含应用程序执行需要的任何信息。你可以使用这些配置文件代替注册表设置项。通常,你会发现使用XML文件比使用注册表少一些错误,并且它们也更灵活。例如,把一个用户从一台计算机迁移到另一台计算机的时候,就不一定需要重新配置应用程序,因为设置信息已经出现在该应用程序的目录中了。在某些角度上,这意味着我们又回到了过去使用INI文件的情形,但是XML文件包含的信息比INI文件多得多,并且会减少一定形式的损坏和配置错误。
当结合其它一些技术(例如可扩充样式表语言转换,XSLT)的时候,XML可以使Web页面更容易阅读。但是,你也能把这类技术应用于内部。例如,很多公司把这种形式的XML作为建立用户使用的帮助文件的一种方法。这种帮助信息作为XML文件中的数据出现,浏览器可以使用XSLT文件把它转换为可视化的输出。应用程序可以使用兼容HTML的控件显示这种信息。至于用户,他们不管数据来自标准的帮助文件或另一个信息源。
最终,XML可能成为一种主要的数据存储技术。有些厂商已经讨论他们的XML产品了。你也可以找到一些XML存储产品,例如微软Office。简单的说,你最终可能发现为了载入数据和显示数据,需要在应用程序中包含分析能力。
生成XML文件
在你能更多地处理XML之前,你需要了解如何格式化XML文件,以及如果生成它。我假定你已经知道了如何格式化XML文件。列表2显示了生成XML的一个简单的方法。
列表2.使用.NET生成XML文件
System::Void btnGenerate_Click(System::Object * sender,
System::EventArgs * e)
{
XmlTextWriter* DataWrite; // 执行实际的数据写入
// 建立数据写入程序
DataWrite = new XmlTextWriter(txtFilename->Text,
System::Text::Encoding::UTF8);
// 写入XML头。当你把这个函数设置为true的时候,.NET建立独立的文档
DataWrite->WriteStartDocument(true);
DataWrite->WriteWhitespace("\r\n");
// 描述测试文件
DataWrite->WriteComment("This is a test document.");
DataWrite->WriteWhitespace("\r\n");
// 启动文档。你必须指定true来建立一个有根元素的新文档。
// 包含根元素失败将导致一个错误产生。
// 本示例包含了一个名字空间前缀、元素的本地名称和于名字空间关联的URL。
DataWrite->WriteStartElement("Data",
"MyData",
"http://www.mysite.com/");
DataWrite->WriteWhitespace("\r\n");
// 把数据写入文档
DataWrite->WriteElementString("DataString1", txtData->Text);
DataWrite->WriteWhitespace("\r\n");
// 用特定的属性写入相同的数据
DataWrite->WriteStartElement("DataString2");
DataWrite->WriteAttributeString("AnAttribute", "AttributeValue");
DataWrite->WriteString(txtData->Text);
DataWrite->WriteEndElement();
DataWrite->WriteWhitespace("\r\n");
// 终止起初的元素或文档的根元素。
DataWrite->WriteEndElement();
// 终止文档
DataWrite->WriteEndDocument();
// 关闭文档。该调用包括把缓冲区的内容写入磁盘的功能。
DataWrite->Close();
// 显示文档是完整的。
MessageBox::Show("File Generated",
"XML Generation Results",
MessageBoxButtons::OK,
MessageBoxIcon::Information);
}
在这个例子中,你建立XmlTextWriter对象--DataWrite的时候, XML写入事务就开始了。你使用了特定的方法写入XML文档输出。.NET框架组件要求你使用某些方法生成良好格式化的XML。例如,你必须使用WriteStartDocument()方法来定义文档的开始,它是XML头。当你使用WriteEndDocument()方法来关闭这种标志,使用Close()把文档缓冲区的内容写入磁盘,并执行实际的文件关闭的时候,写入对话终止。
XML文档需要根元素。第一个WriteStartElement()调用和相关的WriteEndElement()调用建立这种根元素。示例给出了根元素中的两种类型的子元素。第一种演示了如何使用WriteElementString()方法写入把没有属性的字符串。第二种演示了如何使用WriteAttributeString()方法给元素添加属性。
示例也包含了少量可选的元素。有些开发者不关心输出文件是如何出现的,但是我通常在文本编辑器中查看输出。使用WriteWhitespace()方法添加一个回车符和制表符(如果希望的话)将使结果文件更容易在文本编辑器中阅读。当然,类似Internet Explorer的应用程序和Visual Studio IDE不会关心空格的。
你也可能希望在记录文件中包括注释。你可以使用WriteComment()方法完成这种事务。图1显示了输出在Internet Explorer中查看的时候看到的生成循环。
解析XML文件
如果以后你不能读取示例应用程序中的XML输出以提取它包含的所有信息,那么它就不够好。有些开发者试图把XML文件分析为实体,但是实际上用两个步骤更容易实现这个过程。首先,打开文件并得到一个节点。其次,仅仅处理这个节点而不管剩余的节点。列表3演示了这个处理过程的第一部分:得到一个节点。
列表3.XML文件分析概观
System::Void btnParse_Click(System::Object * sender,
System::EventArgs * e)
{
XmlTextReader* DataRead; // 载入数据以供读取
StringBuilder* Output; // 分析后的输出信息
// 打开文件以读取头
DataRead = new XmlTextReader(txtFilename->Text);
// 初始化输出
Output = new StringBuilder();
// 继续读取头节点直到完成
while (DataRead->Read())
{
// 得到每个节点的信息
Output->Append(ProcessEntry(DataRead));
Output->Append("\r\n");
}
// 显示结果
MessageBox::Show(Output->ToString(),
"XML Parsing Results",
MessageBoxButtons::OK,
MessageBoxIcon::Information);
// 关闭文档
DataRead->Close();
}
当代码通过建立新的XmlTextReader打开文档的时候这个过程就开始了。打开XmlTextReader的时候把文件的指针放在文件的开头了。你必须执行一些其它的事务(例如使用上面显示的Read()方法)来得到第一个节点。如果你试图执行这个过程而没有使用Read()或其它的可以接受的方法,应用程序会产生空(null)引用的错误。
示例通过调用ProcessEntry()方法(列表4所示)每次处理一个节点。它把节点的数据追加到StringBuilder对象(Output)。Read()方法一直返回true,直到测试程序处理了文件中的所有节点。这个时候,处理循环结束,示例调用Close()方法关闭文件。你需要确保执行了最后一步。
ProcessEntry()方法执行这个过程的第二步:从单个节点中得到数据(如列表4所示),这个方法检索你需要处理的数据的通用信息。
列表4.XML文件分析细节信息
StringBuilder* ProcessEntry(XmlTextReader* Reader)
{
StringBuilder* DataOut; // 得到输出
// 初始化输出
DataOut = new StringBuilder();
// 检测节点类型
switch (Reader->NodeType)
{
case XmlNodeType::Attribute:
DataOut->Append("Attribute");
break;
// ...处理其它类型...
case XmlNodeType::XmlDeclaration:
DataOut->Append("XML Declaration");
break;
default:
DataOut->Append("Type Unknown");
}
// 添加元素名称
DataOut->Append("\t");
DataOut->Append(Reader->Name);
// 添加元素的值
if (Reader->HasValue)
{
DataOut->Append("\t");
DataOut->Append(Reader->Value);
}
// 添加属性
if (Reader->HasAttributes)
for (Int32 Counter = 0;
Counter < Reader->AttributeCount;
Counter++)
{
Reader->MoveToAttribute(Counter);
DataOut->Append("\r\n\t");
DataOut->Append(Counter);
DataOut->Append("\t");
DataOut->Append(Reader->Name);
DataOut->Append("\t");
DataOut->Append(Reader->Value);
}
// 返回结果
return DataOut;
}
ProcessEntry()方法从通过使用NodeType属性检测节点的类型开始。示例输出一个标识节点类型的字符串,但是作为产品的应用程序通常使用节点类型定义处理操作或检测它是否需要执行处理过程。在这种情况下,示例建立DataOut StringBuilder对象来保持节点类型信息。
每个节点都有名称,因此下面的文本把Name属性值添加到DataOut。名称值可以由你指定(例如图1中的DataString1),或者由.NET框架组件指定默认值。例如,注释没有准确的名称,因此.NET框架组件赋予它的名字是Comment。
大多数节点也有值。但是,你必须使用HasValue属性检验当前节点是否有值。如果示例检测到了当前节点的值,它就把节点的Value属性添加到DataOut。
有些节点也有属性。但是,在使用HasAttributes属性检查他们之前,你必须检查他们的属性。AttributeCount属性表明了某个节点有多少个属性(可以多于一个)。这是一个零值为基础的值。示例使用MoveToAttribute()方法把当前属性载入读取程序。接着代码就可以正常使用Name和Value属性了。
请注意:你不必执行详细的属性处理。如果你需要的是属性值,使用方Reader->GetAttribute()法就可以了,该方法仅仅返回所有属性的值并能节省少量处理步骤。但是,一般情况下你需要同时知道属性的名称和值,因此列表4中的处理技术比你现在使用的其它技术更加通用。图2显示了这个程序的典型输出信息。
注意:图2中显示了少量你可能没有估计到的信息。例如,XML头中的信息(类似版本号),对.NET框架组件来说它是作为属性出现的。示例代码也包含了列表1中的清单文件的副本。你可以在Filename字段中输入这个文件的路径和名称。应用程序分析这个文件与它分析自己生成的XML文件的容易程度是一样的。实际上,这个应用程序可以处理任何良好格式化的XML文件。
总结
现在你已经知道了.NET中使用XML文件是多么容易了。读取和写入XML文件并不比处理一个典型的文本文件复杂。此外,你可能发现了少量以前没有考虑过的XML的新的用法。随着微软把对XML的支持添加到更多的产品中,你可以确性在.NET中分析XML的简便性将使你的开发工作更加容易。