XML文件间的相引用

发贴心情 在一个xml中包括另外一个xml文件
show toc
欢迎来到 MSDN > 数据
将 XML 文档与 XInclude 合并在一起
发布日期: 5/20/2004 | 更新日期: 5/20/2004
Oleg Tkachenko

 

2004 年 4 月

适用于: 
可扩展标记语言 (XML) 1.0 
Microsoft .NET 框架

摘要:本文探讨了如何从多个文档构造单个 XML 文档的问题。它重点讨论了 XML Inclusions (XInclude),这是一种便于您用 XML 实现模块化的多用途机制。(15 页打印页)

下载 XInclude.NET-1.2.exe 示例代码。

 

本页内容
 简介 
 为什么要使用 XInclude? 
 50,000 英尺以外的 XInclude 
 XInclude 语法 
 XInclude 处理模型 
 XPointer 
 XInclude 用法实践 
 小结 
 致谢 

简介
模块化编程的思想要求您将任务细分成小的可管理单位。这种思想也适用于制作 XML 文档。通常,从几个较小的文档生成一个大型文档是有意义的。需要这种模块化方法的一些情况包括:将多个章节编撰成一部书,从单独维护的文档生成网页,或将标准页脚(例如,公司的免责声明)添加到文档中。

有很多方法可以解决如何从多个文档构造出单个 XML 文档的问题。本文重点讨论一个方法,该方法注定是便于用 XML 实现模块化的通用多用途机制:XML Inclusions (XInclude)。

返回页首
为什么要使用 XInclude?
读者可能会问的第一个问题是:“为什么要使用 XInclude,而不是 XML external entities?”答案是,XML 外部实体有很多众所周知的局限和不便于使用的含义,这些因素极大地妨碍了 XML 外部实体成为多用途包含工具。具体来说: 

• XML 外部实体无法成为一个成熟的独立 XML 文档,因为它既不允许独立的 XML 声明,也不允许 Doctype 声明。这实际上意味着 XML 外部实体本身无法包括其他外部实体。 
 
• XML 外部实体必须是格式规范的 XML(第一眼看起来好象没有这么差,但想象一下怎样将示例 C# 代码包括到 XML 文档中)。 
 
• 未能加载外部实体是重大错误 (fatal error);严格禁止任何恢复。 
 
• 只能包括整个外部实体,无法只包括文档的一部分。 
 
• 外部实体必须在 DTD 或内部子集中进行声明。这将打开有很多含义的潘多拉盒子,例如,这些含义可能是:要求文档元素必须在 Doctype 声明中命名,以及对读取方的验证可能需要在其他文档的 DTD 中定义文档的全部内容模型。 
 

人们认识到将 XML 外部实体用作包含机制的缺点已经有一段时间了,实际上,这些缺点导致了在 1999 年由 Microsoft 和 IBM 将 XML Inclusion Proposal 提交到 W3C。该建议定义了一个多用途 XML 包含工具的处理模型和语法。

几年后,XML Inclusions(也称为 Xinclude)的 1.0 版本成为 Candidate Recommendation,这意味着 W3C 相信它已经被广泛审阅并且解决了它要解决的基本技术问题,但它还没有成为正式建议。

那么,XInclude 真的解决了上述问题吗?当然。让我们来看它是如何做到的。

返回页首
50,000 英尺以外的 XInclude
XInclude 定义了一个便于在 XML 文档中实现模块化的多用途包含机制。包括过程被正式定义为将很多 XML 信息集 (XML Infoset) 合并到单个复合的 XML 信息集中。作者通过包含指令来指定要合并哪些文档并控制合并过程。包含指令的 XInclude 语法基于大家熟悉的、容易产生和处理的 XML 构造:元素、属性和 URI 引用。 

XInclude 支持包含非 XML 文本文档,并允许作者控制恢复过程。例如,您可以提供要包含的默认内容或备用文档,如果无法加载远程资源,就将包括这些默认内容或备用文档。

XInclude 还支持部分 XML 包含,也就是说,您可以定义(通过提供 XPointer 指针)应当包括 XML 文档的哪一(些)部分。

下面的基本 XInclude“Hello World”示例可以更清楚地演示这个机制。假如您有一些网页定义,并且想让它们全都包括带有公司版权信息的模板页脚:

page.xml:



Hello world!
   http://www.w3.org/2003/XInclude"/>

footer.xml:


? Contoso Corp, 2003

page.xml after XML Inclusion processing:



Hello world!
   ? Contoso Corp, 2003

 

图 1“Hello World!”XInclude 示例。


下面是另一个介绍性示例,它演示了如何将外部非 XML 文本数据包含到 XML 文档中。假设您有一个存储在服务器上的 XML 文档,并且您想让它包含一个计数器,该计数器描述文档访问的次数:


http://www.w3.org/2003/XInclude">
  

This document has been accessed
  http://www.contoso.com/Counter.aspx?pid=catalog" parse="text"/> times.


这是 XML Inclusion 处理后的文档:


http://www.w3.org/2003/XInclude">
  

This document has been accessed
  45453 times.


返回页首
XInclude 语法
XInclude 语法非常简单,只是在 http://www.w3.org/2003/XInclude 命名空间中的两个元素,即 include 和 fallback。常用的命名空间前缀是“xi”(但可以根据喜好自由使用任何前缀)。对于那些习惯使用正式语法定义的人,XInclude 规范提供了 XInclude 的 XML 架构和 DTD。对于其他人,下面是它的摘要:

xi:include 元素

xi:include 元素充当包括指令。它定义了要包括哪些文档以及如何包括。它的属性是: 

• href — 对要包括的文档的 URI 引用。 
 
• parse — 它的值可以是“xml”或“text”,用于定义如何包括指定的文档(是作为 XML 还是作为纯文本)。默认值是“xml”。 
 
• xpointer — 这是一个 XPointer,用于标识要包括的 XML 文档部分。如果作为文本包括 (parse="text"),将忽略该属性。 
 
• encoding — 作为文本包括时,该属性提供所包括文档的编码提示信息。 
 
• 注 请回忆一下,通常,没有人可以说出任意一个文本文件的编码是什么。幸运的是,XML 不受这个问题的影响(因为如何检测 XML 文档的编码有严格的规则),这就意味着如果是作为 XML (parse="xml") 完成包括的,就可以忽略 encoding 属性。
 
• accept、accept-charset 和 accept-language — 这些属性可以用来帮助进行 HTTP content negotiation。当 XInclude 处理器通过 HTTP 协议取得资源时,它应当使用这些属性的值来设置 HTTP 请求中的 Accept、Accept-Charset 和 Accept-Language 头。如果对于相同的资源,单个 URI 可能基于 HTTP 头分析返回不同的表示形式(比如说,原始 XML 或 XHTML、ISO88591 或 UTF-8 编码、英语版本或希伯来文版本),那么该工具应该针对一种情况。 
 

xi:include 元素可能只包含一个可选的 xi:fallback 元素,任何其他内容仅仅被忽略。

xi:fallback 元素

xi:fallback 元素提供了一个在丢失资源后恢复的机制。如果要包括的资源由于任何原因(连接问题、安全限制、资源不存在、URI 架构未知或者像 mailto: 一样不可获取,等等)而不可用,那么将包括 xi:fallback 元素的内容。下面的示例显示了如何使用 xi:fallback 元素: 

http://www.w3.org/2003/XInclude">
   

New This Week from MSDN

http://msdn.microsoft.com/rss.xml">
      Sorry, MSDN news are unavailable.
   

如果资源丢失并且 xi:include 元素为空,则不会包括任何内容。xi:fallback 元素没有属性,它必须是 xi:include 元素的直接子集,并且它的内容是不受限制的。由于对 xi:fallback 元素的内容没有限制,因此,它就可以包含另一个 xi:include 指令,从而提供要包括的可选恢复资源:

http://www.w3.org/2003/XInclude">
   

New This Week from MSDN

   http://msdn.microsoft.com/rss.xml">
      
         http://msdn.microsoft.com/xml/rss.xml">
            Sorry, MSDN news are unavailable.
         
      
   

保存基本 URI

留心的读者可能已经注意到出现在结果文档中的 xml:base 属性。这是一个重要的功能,当文档被包括到另一个文档(可能在其他位置)中时,它可以防止在被包括的文档中的相对 URI 引用被破坏。请参考下例。

位于 C:/Contoso/Inventory 目录中的 parts.xml 文档使用相对 URI 引用来引用相同目录中的 XML 架构 parts.xsd:

http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="parts.xsd">
    

位于 C:/Contoso/Reports 目录中的 report.xml 文档包括 parts.xml:


   
               xmlns:xi="http://www.w3.org/2003/XInclude"/>
   

经过 XML Inclusion 处理之后,report.xml 文档如下所示:


    
        http://www.w3.org/2003/XMLSchema-instance" 
xsi:noNamespaceSchemaLocation="parts.xsd" 
xml:base="../Inventory/parts.xml">
              
        
    

注意 xml:base 属性是如何保存被包括文档的基本 URI 的,该属性使 xsi:noNamespaceSchemaLocation 属性中的相对 URI 引用指向位于 C:/Contoso/Inventory 目录中的相同架构文件。

返回页首
XInclude 处理模型
从技术上讲,XInclude 将包含定义为特殊类型的 XML 信息集转换。源信息集转换为结果信息集后,在结果信息集中每个 xi:include 元素被替换为它引用的信息集。该过程也可以被看作是信息集的合并。

XML Inclusion 是一个递归过程,因此,还会处理被包含文档中的每个 xi:include 元素。当然,为了保证安全,循环包含将会被检测,并将其当作重大错误。什么是重大错误?XInclude 定义了两种类型的错误,这两类错误都可能在包含过程中发生:“重大错误”,可以想象它是指出现了使正常处理过程无法继续的因素;和“资源错误”,这是指获取资源的尝试失败。重大错误是致命的,这就意味着处理过程必须停止,而资源错误则可后进行处理。

现在来看看如何处理 xi:include 元素。首先,它依赖于 parse 属性值。当 parse="xml" 时(顺便说一句,这是默认值),获取引用的文档,并将该文档解析为 XML,然后替换源信息集以及其后代中的 xi:include 元素。可以设想,试图包含格式不规范的 XML 文档将导致重大错误,因为格式的规范性是 XML 的“圣杯”(译注:Holy Grail — 圣杯/圣盘,据中世纪传说为基督在最后的晚餐上用过的那个杯或盘子,后来成为许多骑士追求的目标,此处是指格式的规范性是编写 XML 文档时追求的目标)。

如果 parse="text",将获取引用的文档,并将它视为纯文本,然后以同样方式替换 xi:include。这是非常有用的功能,例如,它允许将源代码或 XML 文档作为文本进行包含。如下示例显示了将 XML 文档作为文本包含在另一个文档中:

http://www.w3.org/2003/XInclude">
   For instance, consider the following SOAP message request to the Contoso Delivery Web Service: 
   

执行 XML Inclusion 之后,结果文档如下所示:

http://www.w3.org/2003/XInclude">
For instance, consider the following SOAP message request to the Contoso Web Service:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <Delivery xmlns="http://www.contoso.com">
      <address>
        <Street>One Microsoft Way</Street>
        <City>Redmond</City>
        <Zip>98052</Zip>
      </address>
    </Delivery>
  </soap:Body>
</soap:Envelope>

注意左尖括号字符被转义,因为整个 contoso-soap.xml 文档(尽管它是 XML)是作为单个文本节点被包含的。

在另一个文本包含可能有用的有趣事例中,您希望在 XSL 转换期间包含某个外部文本文档。虽然 XSLT 1.0 有 document() 函数,但该函数只允许您打开外部 XML 文档,而不能打开纯文本文档。例如,要将 CSS 文件包含到生成的 HTML 文档中,只要一个 xi:include 指令就足够了:


   
      
         
            
         
         ...

由于显而易见的原因,您仍然无法用该方式包含二进制资源。试图包含 XML 1.0 建议所不允许的字符将导致重大错误。

返回页首
XPointer
到此为止,都很顺利。上面提供的示例说明了如何包含整个文档,但如果只需包含文档的一部分(比如说,包括某个书籍目录中特定作者的所有书籍),又该怎么办呢?请输入 XPointer。

XInclude facilitates partial inclusion using XPointer pointers in xpointer attribute:

My favorite books are:

上面的指令包含了所有 book 元素,它们的 author 属性值是来自 books.xml 文档的“Stephen King”。圆括号中的表达式您应当非常熟悉。它是 XPath 吗?不完全对,但非常接近。它是特殊类型的 XPointer,或更准确地说,是指向特殊 XPointer 架构 xpointer() 的指针,而 xpointer() 是 XPath 的扩展。现在让我们循序渐进地看一看 XPointer 的原理。

XPointer 代表“XML 指针语言”,它是用于进行 XML 寻址的可扩展的系统。XPointer 的基础是 XPointer 框架,后者定义了段落标识符的语义和基本语法。XPointer 框架定义了两种类型的指针:快捷 (shorthand) 指针和基于架构 (scheme-based) 指针。

快捷指针(以前称为 barename)事实上是对HTML 中段落标识符的大致模拟,后者指向命名定位点。而 XPointer 快捷指针最多只能按元素 ID 标识 XML 文档中的一个元素。ID 可以由 DTD、XML 架构或以应用程序特有的方式确定。

xpointer 属性中的 bk101 值就是一个快捷 XPointer 指针,它标识了 books.xml 文档中的某个元素,该元素的 ID 是“bk101”。

基于架构的指针是更复杂的指针类型,它由指针部分的序列组成,并可选择用空格分开。每个指针部分有一个架构名称和一些放在圆括号中的、架构特有的数据,例如 element(bk101) 或 xpointer(//book):

在以前的包含指令中,xpointer 属性值表示基于架构的 XPointer 指针,该指针标识了在 books.xml 文档(其 ID 是 bk101)中的元素,或者如果无法找到这样的元素,则它标识其 title 属性值是 Dreamcatcher 的元素。 

 

图 2基于架构的 XPointer 指针结构


指针部分是按从左到右的顺序计算的,直到某个部分识别出某个子资源。无法识别或不被支持的部分将被忽略。但要注意,如果整个指针中没有任何指针部分能够识别子资源,则会发生错误。快捷指针同样如此。

如您所见,基于架构的指针提供了一种使段落标识更可靠的方式,因而允许您指定几个指针部分,其中每个部分可以按不同方式指向希望的子资源。如果指针部分未能识别子资源,则继续计算下一个指针部分,实际上提供某种替换的寻址行为。

XPointer 架构

W3C 定义了三种类型的 XPointer 架构,即 element()、xmlns() 和 xpointer()。另外,Simon St.Laurent 建议 xpath1() scheme,该架构使用规则的 XPath 1.0 语法,就像在 xpath1(//book) 中一样。

此 element() 方法便于按 ID(就像快捷指针一样)和按同辈元素之间的位置编号进行基本的 XML 元素寻址。例如,element(/1/3) 指针部分标识了根元素的第三个子元素,而 element(bk101/2) 则标识了 ID 为 bk101 的元素的第二个子元素。

xmlns() 架构表示辅助指针部分,它永远不标识自己的任何子资源,但允许定义绑定了其他指针部分的上下文的命名空间(这样,就可以根据需要将任意多个指针部分组合在一起)。例如,下面的 XPointer 指针标识了在属于“http://www.contoso.com”命名空间的文档中的所有 book 元素:

xmlns(co=http://www.contoso.com)xpointer(//co:book)

xpointer() 架构的功能最强大。它为对 XML 文档的各部分进行寻址提供了高级别的功能。它基于 XPath 1.0,并将 XPath 扩展为允许对字符串、点和范围进行寻址。请注意,xpointer() 架构仍然在发展,并且很可能有进一步的变化。

现在回到对 XInclude 的讨论。XInclude 规范要求任何符合规范的 XInclude 处理器都必须支持 XPointer 框架和 element() 架构。这实际上意味着您只能安全地使用文档中的快捷指针和基于 element() 架构的指针,但既然 Xinclude 还在发展之中,所以最好经常查阅 XInclude 处理器的文档。

我们已经讨论完理论部分。现在谈谈实践中如何编程。

返回页首
XInclude 用法实践
我们首先需要支持 Xinclude 的环境。不要指望您喜欢的 XML 语法分析程序自动支持 XInclude。例如,很遗憾 MSXML 和 System.xml 都不支持 Xinclude。W3C 在 XInclude Implementations Report 中列出了可用的 XInclude 实现。其中一些实际上仅用于实验,但有几个具有生产水准。 

我最喜欢的 XInclude 实现 XInclude.NET,这可能因为我是此项目的首席开发人员。

用于 .NET 的 XInclude — XInclude.NET

XInclude.NET 项目受到 Chris Lovett 的 XInclude, Anyone? 一文启发,发现这篇文章时,我正在解决类似的问题:如何利用由不同团队分别编写的文档编撰出网页。通过方便地利用 .NET 框架的强大功能,可以在 System.Xml API 之上建立健壮、高性能的 XInclude 处理器。XInclude.NET 库的 1.2 版本最近已经发布,现在用户可以判断是否已实现该目标。

XInclude.NET 项目是 2003 年 11 月 10 日发布的 XInclude 1.0 Last Call Working Draft (编写本文时的最新版本)的实现,是用 .NET 框架的 C# 编写的。此外,它支持 XPointer 框架、element()、xmlns()、xpath1() 和 xpointer()(仅 XPath 子集)架构。本文所附的代码示例包含 XInclude.NET 1.2 版本,该版本提供了预编译的 XInclude.dll 程序集、API 文档、示例和 XInclude 实现的源代码。 

XInclude.NET 中的关键类是 XIncludingReader,可以在 GotDotNet.XInclude 命名空间中找到它。主要设计目标是为 XML 处理建立可插接的流管道。为了实现这个目标,XIncludingReader 实现了一个 XmlReader,该 XmlReader 可以围绕在另一个 XmlReader 的周围。该体系结构允许在不作任何重大修改的情况下将 XInclude 处理层很容易地插入到各种应用程序中。例如,当 XML 正在加载到 XmlDocument 中时,如果要启用对该 XML 的 XInclude 处理,只需用 XIncludingReader 将 wrapXmlTextReader 包装起来:

using System.Xml;
using GotDotNet.XInclude;
public class Class1 {
    public static void Main(string[] args) {
        XmlTextReader r = new XmlTextReader("document.xml");
        XIncludingReader xir = new XIncludingReader(r);
        XmlDocument doc = new XmlDocument();
        doc.Load(xir);          
        //...
    }
}

在执行 XSL 转换之前的 XML Inclusion 是同样简单的:

using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
using System.IO;
using GotDotNet.XInclude;
public class Class1 {
    public static void Main(string[] args) {        
        XIncludingReader xir = new XIncludingReader("document.xml");
        XPathDocument doc = new XPathDocument(xir);
        XslTransform xslt = new XslTransform();
        xslt.Load("stylesheet.xslt");
        StreamWriter sw = new StreamWriter("result.html");
        xslt.Transform(doc, null, sw);
        sw.Close();        
    }
}

前面显示的 CSS 包含的示例假定 XSLT 样式表由 XInclude 处理器处理;以下代码显示了如何完成该操作:

public class Class1 {
    public static void Main(string[] args) {                
        XPathDocument doc = new XPathDocument("document.xml");
        XslTransform xslt = new XslTransform();
        XIncludingReader xir = new XIncludingReader("stylesheet.xslt");
        xslt.Load(xir);
        StreamWriter sw = new StreamWriter("result.html");
        xslt.Transform(doc, null, sw);
        sw.Close();        
    }
}

XML Inclusion 过程与 XML 语法分析、验证或转换没有关系。这实际上意味着由您决定在什么时候允许 XML Inclusion 发生:是在语法分析之后,但在验证之前;还是在验证之后,但在转换之前,甚至在转换之后。

注 在执行 XML Inclusion 期间保存基本 URI 意味着在结果文档中会出现 xml:base 属性。这实际上意味着在“预验证包含”方案中 XML 架构作者应当期望 xml:base 属性出现在所包括的元素的最上层。实际上,xml:base 属性是核心标准 XML 属性,所以在这里有必要允许在架构中对每个元素包含 xml:base 属性。

XIncludingReader 如何工作

注 必须熟悉 XmlReader 体系结构。否则可以跳过本节。

XIncludingReader 实现基于 XmlReaders 链 (chaining) 技术,.NET Framework Developer's Guide 中的 Customized XML Reader Creation 一节对此进行了描述。该技术与旧式 SAX 筛选很相似,它允许通过委托调用将 XmlReaders 进行链化,从而有效地提供高性能的方法来筛选或修改 XML(当 XML 正在被读取时)。

 

图 3 一条 XmlReaders 链


XIncludingReader 总是工作在另一个读取器(SAX 术语叫“父”读取器,它为 XIncludingReader 提供输入 XML 流)之上。当 XIncludingReader 的输入与 XmlReader 不兼容时(如在 System.IO.Stream 或 System.IO.TextReader 中),将实例化一个临时 XmlTextReader 对象来充当父读取器。所得到的 XML(在完成 XML Inclusion 的位置)可以被 XIncludingReader 自己的客户端应用程序读取;记住,它只是 XmlReader。 

大多数时候,XIncludingReader 不做任何事情;它只是向父读取器委派方法调用,并透明地公开父读取器的属性(例如,名称或值)来作为它自己的属性。虽然这样,但它总是监视父读取器所报告的 XML 元素的名称。一旦将父读取器放在 xi:include 元素上,XIncludingReader 就会被激活并开始执行包含过程。

父读取器并不会将 xi:include 元素公开给客户端应用程序,而是被压入堆栈,并实例化新的临时 XmlReader 以读取 xi:include 指令所引用的文档。该临时读取器的实际类型取决于是否存在 xpointer 属性(如果是这样,它会成为一个专用的 XPointerReader,它将实现 XPointer 并只读取指针节点所标识的内容),或者取决于包含操作是否是在文本模式下完成的(如果是这样,它会成为一个专用的 TextIncludingReader,它将把整个文档作为单个文本节点进行读取)。否则,它的行为只像一个 XmlTextReader。

不管怎样,父读取器都将被压入堆栈,而这个新的读取器会成为父读取器。普通读取会继续进行下去,直到从父读取器读取不到输入。之后,包括过程执行完毕,并且前一个父读取器从堆栈中弹出。

虽然这听起来很简单,但请记住这只是冰山一角。隐藏的部分超出了本文的范围,这些部分包括实现后备处理和极其重要地公开 XmlReader API 中的综合 xml:base 属性(Martin Gudgin 在他的网络日记中对此进行了详细介绍)。如果想要了解更多内容,只需深入阅读源代码。另外,请在该项目的 message board 上大胆地询问任何与 XInclude.NET 有关的问题,并将错误提交到 bug tracker。

这样的仅向前、非缓存的流实现会占用非常少的内存,并且几乎不会对性能造成任何不利影响。在大多数时间里,XIncludingReader 所做的全部操作只是比较元素的名称、监视 xi:include 元素。在考虑到 XmlNameTable 的情况下,假设比较已经完成,这将是非常廉价的操作(因此,在 99% 的情况下这只是对两个对象指针进行的比较)。 

遗憾的是,对 XPointer 指针的处理并不是低成本的。事实上,它需要将整个包含的文档加载到内存中,只是为了按 ID、XPath 选择路径或按 xpointer() 指针选择所需的部分。有趣的是,对 XPointer 指针的计算是通过将 XPointer 语法转换成 XPath 表达式在内部实现的,如下所示: 

1.
 快捷指针被转换成 id() function 调用:从 bk101 到 id('bk101')。 
 
2.
 基于 element() 架构的指针被转换成 XPath 选择路径,其中包含 id() 函数调用和/或位置谓词:从 element(bk101)/3 到 id('bk101')/*[3]。 
 
3.
 基于 xpointer() 架构的指针被直接用作 XPath 表达式。这就是为什么 XInclude.NET 不支持 XPath 的 xpointer() 超集的原因。 
 

未来的 XInclude.NET 版本可能提供更完整和更有效的 XPointer 实现。

返回页首
小结
XInclude 是正在发展的 W3C 标准,旨在便于实现 XML 中的模块化。它定义了对 XML 文档进行多用途包含或合并时所需的处理模型和语法。它不依赖于 XML 语法分析和验证,并且它的语法基于 XML 元素、属性和 URI 引用。 

尽管 XInclude 还不是 W3C 推荐标准,但它的实现数量以及它在 XML 社区受到的欢迎正在不断增长。在本文中,我们已经介绍了 XInclude 和 Xpointer。我们还会讨论针对 .NET 框架的具体的 XInclude 实现。

返回页首
致谢
我十分感谢 Dare Obasanjo 在我准备本文时提供的全部帮助。

转到原英文页面


返回页首 

 适合打印机打印的版本 通过电子邮件发送此页面 添加到收藏夹 备注
个人信息中心 |MSDN中文速递邮件 |联系我们
©2004 Microsoft Corporation. 版权所有.  保留所有权利 |商标 |隐私权声明  
  


name="google_ads_frame" marginwidth="0" marginheight="0" src="http://pagead2.googlesyndication.com/pagead/ads?client=ca-pub-1683803802533351&dt=1169784058698&lmt=1169784058&prev_fmts=468x15_0ads_al_s&format=300x250_as&output=html&channel=9020077191&url=http%3A%2F%2Fbbs.xml.org.cn%2Fdispbbs.asp%3FboardID%3D1%26ID%3D10119&color_bg=FFFFFF&color_text=000000&color_link=000000&color_url=0066FF&color_border=FFFFFF&ad_type=text_image&ref=http%3A%2F%2Fwww.google.com%2Fsearch%3Fq%3DXML%25E4%25B8%25AD%25E6%2580%258E%25E4%25B9%2588%25E5%25BC%2595%25E7%2594%25A8%25E5%258F%25A6%25E5%25A4%2596%25E4%25B8%2580%25E4%25B8%25AAXML%25E6%2596%2587%25E6%25A1%25A3%26hl%3Dzh-CN%26lr%3D%26nxpt%3D20.413230410049825984035&cc=10&u_h=1024&u_w=1280&u_ah=994&u_aw=1280&u_cd=32&u_tz=480&u_java=true" frameborder="0" width="300" scrolling="no" height="250" allowtransparency="allowtransparency">

----------------------------------------------

<个人签名>
  
www.koolle.com

aloning(at)gmail.com
  29519680

你可能感兴趣的:(杂七杂八,xml,文档,include,encoding,框架,xsl)