技巧: 使用 XSLT 身份模板转换 XHTML

合理关闭 XHTML 标签

XHTML 不仅仅是结构良好的 HTML:某些标签必须合理关闭。合理关闭的标签是有效的 XML,但是浏览器可能不会正确地解析它们,给动态的 Web 2.0 特性带来问题。不管是将 XML 转换为 XHTML,还是仅仅过滤 XHTML,都要探究创建正确的 XHTML 所需的 XSLT 模板,遵循用于 XHTML 的 W3C 推荐实践。

Doug Domeny, 高级软件工程师, Freelance

Doug Domeny 使用 XSLT、W3C XML Schema、DHTML、JavaScript、jQuery、正则表达式和 CSS 编写开发了基于浏览器的、多语言的、对业务用户友好的 XML 编辑器。他持有韦纳姆戈登学院的计算机科学和数学学士学位,曾多年服务于 OASIS 技术委员会,比如 XML Localization Interchange File Format (XLIFF) 和 Open Architecture for XML Authoring and Localization (OAXAL)。在作为软件工程师期间,他发展了在软件工程设计和架构、UI 设计和技术写作方面的重要技能。



2010 年 12 月 21 日

常用缩略词

  • HTML:超文本标记语言
  • W3C:万维网联盟
  • XHTML:可扩展超文本标记语言
  • XML:可扩展标记语言

XHTML 是编写为结构良好的 XML 的 HTML,这通常意味着 HTML 必须坚持遵循 XML 规则。这些规则比用于 HTML 的那些更严格 — 例如:

  • 标签名称是区分大小写的,特别是小写 — 例如, <p> 而非 <P>
  • 引用属性值 — 例如, <input type="checkbox"> 而非 <input type=checkbox>
  • 如果应用了一个属性,就要提供一个值。对于没有明确值的 HTML 属性,将属性名称作为其值 — 例如,<input selected="selected"> not <input selected>。无用的属性包括:
    • checked
    • disabled
    • selected
    • nowrap
  • 合理嵌套标签 — 例如, <b><i>…</i></b> 而非 <b><i>…</b></i>
  • 不要忽略可选结束标记 — 例如, <p>…</p><p>…</p> 而非 <p>…<p>…

然而在某一方面,XML 没有 HTML 严格 — 即关闭标签的方式。对于 XML,您可以使用具有单独结束标记的短形式(自闭合)或长形式选择空元素(即没有文本或其它标签在其中的任何元素):

  • 正斜杠前带有或没有空格的短形式(/):<tag/><tag />
  • 长形式:<tag></tag>

但是对于 HTML,有些标签需要结束标记,而其他的不需要。

需要 结束标记的标签包括 <a><abbr><acronym><address><b><big>, <blockquote><button><code><dir><div><em><font><form><h1><i><label><li><map><ol><pre><script><span><strong><style><sub><table><tt><ul><xml>禁止使用 结束标记的标签包括 <area><base><br>, <col><frame><img><isindex><link><meta><param>

自闭合的 HTML 标签

自闭合的 HTML 标签包括 <area /><base /><basefont /><bgsound /><br /><col /><frame /><hr /><img /><input /><isindex /><keygen /><link /><meta /><param />

此外,W3C 推荐将空格放在自闭合标签的结尾,以改进与浏览器的兼容性:

  • 推荐:<input type="checkbox" />
  • 不推荐:<input type="checkbox"/>

参见 参考资料 获取 HTML Compatibility Guidelines 的链接。

由于 XHTML 是 XML,XSLT 可以转换 XHTML。XSLT 的原始意图是充当将 XML 数据转换为 HTML 的灵活而强大的方式。对 XML 技术 — 特别是 XHTML — 的广泛接受,增加了 XSLT 解决的应用程序的数量。XHTML 可以是转换的一个输入,或转换所生成的,或者两者兼而有之。使用 XSLT 来生成 XHTML 涉及到如何以符合 HTML 的方式关闭空标签的问题。

合理关闭 XHTML 标签

如何合理关闭了空标签,会发生什么?

  • 如果以短形式关闭,用于下载 JavaScript 文件的脚本标签就无法获得文件。

    失败:<script type="text/javascript" href="myfile.js" />

    成功:<script type="text/javascript" href="myfiles.js"></script>

  • 一个自闭合的空标签 <div> 被视为一个开始标签。自闭合的 <div> 元素捕获以下元素并将其作为自己的文本内容,直至下一个开始标签 <div> 出现为止。例如:

    <div id="mydiv1" />
    
    <p>This paragraph will be
    contained within mydiv1</p>
    
    <div id="mydiv2"></div>
    
    <p>This paragraph will NOT be
    contained in either 'div'</p>

    浏览器按如下方式解译标记,添加了 <div> 隐式结束标记并将其标为注释:



    <div id="mydiv1">
    
    <p>This paragraph is
    contained within mydiv1</p>
    
    </div> <!-- implied closing tag -->
    
    <div id="mydiv2"></div>
    
    <p>This paragraph is NOT
    contained in either 'div'</p>
  • 以长格式 <br></br> 表示的 <br> 元素被解译为两个元素:<br><br>,因此重复了换行符的数量。

处理和复制标签

有三种解决方案可合理关闭 XHTML 标签,具体取决于开发环境。序列化涉及到编写代码(例如,C# 或 Java™ 代码),以将一个 XML 文档对象转化为一个字符串。序列化是最复杂的解决方案,但是也是最灵活的。另两个解决方案取决于 XSLT 的版本(XSLT 2.0 是最简单的解决方案)。


解决方案:XHTML 序列化

序列化 是将内存中的二进制对象转化为适合于在文件系统或通过网络传输的字符串。不管是您将一个对象模型的序列化编码为 XHTML 还是 XSLT 转换的结果已经是一个字符串,都要通过控制序列化解决合理关闭空 HTML 标签的问题。

如果转换的结果是一个对象,以简短、自闭合的形式序列化禁止使用结束标记的标签:

"<" tag-name [ attributes ] " />"

使用一个单独的结束标记关闭所有其他空标签:

"<" tag-name [ attributes ] "></" tag-name ">"

这里有 C# 中的两个例子:一个用于 XmlTextWriter 且另一个用于 a StringWriter。在 清单 1 中,XhtmlTextWriter 衍生自 XmlTextWriter 并重写 WriteEndElement 方法,以便以短形式或长形式关闭元素。

清单 1. XhtmlTextWriter
public class XhtmlTextWriter : System.Xml.XmlTextWriter
{
    private string tagName = string.Empty;
    private string elementNamespace = string.Empty;

    public XhtmlTextWriter(System.IO.TextWriter w)
        : base(w)
    {
    }

    public override void WriteEndElement()
    {
        bool isShortNotation = true;

        // Check if XHTML Namespace
        if (string.IsNullOrEmpty(this.elementNamespace) || 
            (this.elementNamespace.Contains("www.w3.org") && 
                this.elementNamespace.Contains("xhtml")))
        {
            switch (this.tagName)
            {
                case "area":
                    isShortNotation = true;
                    break;
                case "base":
                    isShortNotation = true;
                    break;
                case "basefont":
                    isShortNotation = true;
                    break;
                case "bgsound":
                    isShortNotation = true;
                    break;
                case "br":
                    isShortNotation = true;
                    break;
                case "col":
                    isShortNotation = true;
                    break;
                case "frame":
                    isShortNotation = true;
                    break;
                case "hr":
                    isShortNotation = true;
                    break;
                case "img":
                    isShortNotation = true;
                    break;
                case "input":
                    isShortNotation = true;
                    break;
                case "isindex":
                    isShortNotation = true;
                    break;
                case "keygen":
                    isShortNotation = true;
                    break;
                case "link":
                    isShortNotation = true;
                    break;
                case "meta":
                    isShortNotation = true;
                    break;
                case "param":
                    isShortNotation = true;
                    break;
                default:
                    isShortNotation = false;
                    break;
            }
        }

        if (isShortNotation)
        {
            base.WriteEndElement();
        }
        else
        {
            base.WriteFullEndElement();
        }
    }

    public override void WriteStartElement(string prefix, string localName, string ns)
    {
        this.tagName = localName.ToLower();
        this.elementNamespace = ns;
        base.WriteStartElement(prefix, localName, ns);
    }

    public override void WriteStartDocument()
    {
        // Don't emit XML declaration
    }

    public override void WriteStartDocument(bool standalone)
    {
        // Don't emit XML declaration
    }
}

清单 2 显示 XhtmlStringWriter 类,该类衍生自 StringWriter 且重写 Write 方法,以便为需要的那些标签将短形式转化为长形式。您可以用其他编程语言(比如 Java 语言)编写类似的方法。

清单 2. XhtmlStringWriter
public class XhtmlStringWriter : System.IO.StringWriter
{
    public override void Write(string value)
    {
        bool isShortNotation = false;
        switch (value)
        {
            case "></area>":
                isShortNotation = true;
                break;
            case "></base>":
                isShortNotation = true;
                break;
            case "></basefont>":
                isShortNotation = true;
                break;
            case "></bgsound>":
                isShortNotation = true;
                break;
            case "></br>":
                isShortNotation = true;
                break;
            case "></col>":
                isShortNotation = true;
                break;
            case "></frame>":
                isShortNotation = true;
                break;
            case "></hr>":
                isShortNotation = true;
                break;
            case "></img>":
                isShortNotation = true;
                break;
            case "></input>":
                isShortNotation = true;
                break;
            case "></isindex>":
                isShortNotation = true;
                break;
            case "></keygen>":
                isShortNotation = true;
                break;
            case "></link>":
                isShortNotation = true;
                break;
            case "></meta>":
                isShortNotation = true;
                break;
            case "></param>":
                isShortNotation = true;
                break;
        }

        if (isShortNotation)
        {
            base.Write(" />");
        }
        else
        {
            base.Write(value);
        }
    }
}

解决方案:XSLT 1.0

首先,确保 XSLT 输出方法为 xml,而非 htmlhtml 方法不是 XHTML;HTML 不是 XML。XSLT 处理器或 XML 解析器都不能处理 HTML。

如果转换的结果是一个字符串或文件,编码 XSLT 模板以强行合理关闭空标签,从而间接控制序列化。关闭空标签的方式取决于 XSLT 处理器的实现。

身份模板

如果输入 也是 XHTML,使用身份模板来将未改变的标签复制到输出。身份模板处理输入元素和属性,并将它们复制到输出。没有身份模板,只有标签之间的文本可以复制到输出。

清单 3 中的 XSLT 缺乏身份模板,仅输出纯文本。

清单 3. 结果只能是纯文本
<?xml version='1.0' ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <xsl:apply-templates/>
    </xsl:template>
</xsl:stylesheet>

清单 4 中的 XSLT 有身份模板,可以复制不由其他模板处理的元素。一个身份模板匹配一个节点,并复制该节点。有两种方式可复制元素:本例使用 xsl:copy。另一种方式就是使用 xsl:element,这在稍后将加以讨论。

清单 4. 结果包括标签但可能没有得到合理关闭
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xml" omit-xml-declaration="yes"/>

    <!-- put your templates here -->

    <!-- identity templates -->
    <xsl:template match="*">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates select="node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="@*|text()|comment()|processing-instruction()">
        <xsl:copy/>
    </xsl:template>

</xsl:stylesheet>

控制关闭标签的方式

要合理关闭标签,身份模板必须选择需要短形式的标签。在一个模板的匹配表达式中选择标签需要知道标签的名称空间,或知道没有使用名称空间。控制如何将空标签呈现为短形式或长形式的窍门不是处理子节点(短形式)或处理子节点(长形式),即使没有子节点可处理。在这点上,XSLT 处理器起重要作用。Microsoft 处理器 — Microsoft® .NET 和 MSXML — 使用一种无需处理子节点即可以短形式输出标签的诀窍。其他处理器,比如 Saxon,总是为空标签使用短形式,因此对于需要结束标记的 HTML 元素,必须插入一些文本。对于大部分元素,一个空格很合适。对于 <script> 标签,一个 JavaScript 注释标记(即 //)将开始和结束标记分离开来。所幸,该方法也适用于 Microsoft 处理器。

Microsoft .NET 或 MSXML 处理器

如果输入文档没有名称空间,如 清单 5 所示,那么 XSLT 也不需要一个名称空间。

清单 5. 没有名称空间的 XHTML 输入文档
<html>
...
</html>

清单 6 展示匹配自闭合标签的 XSLT。自闭合标签经过处理,以便 Microsoft XSLT 处理器使用短形式。因为没有名称空间,标签名称没有一个名称空间前缀。

清单 6. 具有名称空间的 XSLT
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" omit-xml-declaration="yes"/>
    <!-- identity templates -->
    <xsl:template match="*">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates select="node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="area[not(node())]|base[not(node())]|
        basefont[not(node())]|bgsound[not(node())]|br[not(node())]|
        col[not(node())]|frame[not(node())]|hr[not(node())]|
        img[not(node())]|input[not(node())]|isindex[not(node())]|
        keygen[not(node())]|link[not(node())]|meta[not(node())]|
        param[not(node())]">
        <!-- identity without closing tags -->
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="@*|text()|comment()|processing-instruction()">
        <xsl:copy/>
    </xsl:template>
</xsl:stylesheet>

如果输入文档有一个名称空间,如 清单 7 所示,XSLT 需要一个名称空间,且标签名需要一个前缀。

清单 7. 具有名称空间的 XHTML 输入文档
<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
...
</html>

清单 8 展示匹配自闭合标签的 XSLT。因为有一个名称空间,所以标签名需要一个名称空间前缀。没有前缀,标签就不会匹配。注意,XHTML 名称空间声明以 xmlns:htm 开头。前缀 htm 是任意的。

清单 8. 具有名称空间的 XSLT
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:htm="http://www.w3.org/1999/xhtml" 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" omit-xml-declaration="yes"/>
    <!-- identity templates -->
    <xsl:template match="*">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates select="node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="htm:area|htm:base|htm:basefont|
        htm:bgsound|htm:br|htm:col|htm:frame|htm:hr|htm:img|
        htm:input|htm:isindex|htm:keygen|htm:link|htm:meta|
        htm:param">
        <!-- identity without closing tags -->
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="@*|text()|comment()|processing-instruction()">
        <xsl:copy/>
    </xsl:template>
</xsl:stylesheet>

任何 XSLT 处理器

如果输入文档没有名称空间,如 清单 9 所示,那么 XSLT 也不需要名称空间。

清单 9. 没有名称空间的 XHTML 输入参数
<html>
...
</html>

清单 10 展示匹配自闭合标签的 XSLT。需要单独结束标记且不为空的标签在输出中带有一个空格,以防止使用短形式序列化它们。一个例外是空元素 script,对其提供了 JavaScript 注释符号(//)。因为没有名称空间,标签名没有一个名称空间前缀。

清单 10. 具有匹配的自闭合标签的 XSLT
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" omit-xml-declaration="yes"/>

    <!-- identity templates -->
    <xsl:template match="*[not(node())]">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:text> </xsl:text>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="script[not(node())]">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:text>//</xsl:text>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates select="node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="area[not(node())]|base[not(node())]|
        basefont[not(node())]|bgsound[not(node())]|br[not(node())]|
        col[not(node())]|frame[not(node())]|hr[not(node())]|
        img[not(node())]|input[not(node())]|isindex[not(node())]|
        keygen[not(node())]|link[not(node())]|meta[not(node())]|
        param[not(node())]">
        <!-- identity without closing tags -->
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="@*|text()|comment()|processing-instruction()">
        <xsl:copy/>
    </xsl:template>
</xsl:stylesheet>

如果输入文档有一个名称空间,如 清单 11 中的 XHTML 文档所示,XSLT 需要一个名称空间,且标签名需要一个前缀。

清单 11. 具有名称空间的 XHTML 输入文档
<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
...
</html>

清单 12 展示匹配自闭合标签的 XSLT。需要单独结束标记且不为空的标签在输出中带有一个空格,以防止使用短形式序列化它们。一个例外是空元素 script,对其提供了 JavaScript 注释符号(//)。因为有一个名称空间,所以标签名需要一个名称空间前缀。没有前缀,标签便不会匹配。注意,XHTML 名称空间声明以 xmlns:htm 开头。前缀 htm 是任意的。

没有优先级的模板支持将匹配表达式用于自闭合标签,以获得更高优先级。没有它,就会忽略自闭合标签的模板。

清单 12. 具有匹配的自闭合标签和名称空间的 XSLT
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:htm="http://www.w3.org/1999/xhtml" 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" omit-xml-declaration="yes"/>

    <!-- identity templates -->

    <xsl:template match="*">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates select="node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="htm:area|htm:base|htm:basefont|
        htm:bgsound|htm:br|htm:col|htm:frame|htm:hr|htm:img|
        htm:input|htm:isindex|htm:keygen|htm:link|htm:meta|
        htm:param">
        <!-- identity without closing tags -->
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*[not(node())]" priority="-0.5">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:text> </xsl:text>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="htm:script[not(node())]">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:text>//</xsl:text>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="@*|text()|comment()|processing-instruction()">
        <xsl:copy/>
    </xsl:template>
</xsl:stylesheet>

控制输出名称空间

要从输出中排除 XHTML 名称空间,比如在转化为另一个 XML 格式时,使用 <xsl:element> 标签,而非 <xsl:copy>,如 清单 13 所示。

清单 13. 不包括输出名称空间的 XSLT 模板
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:htm="http://www.w3.org/1999/xhtml" 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <!-- identity templates -->
    <xsl:output method="xml" omit-xml-declaration="yes"/>

    <xsl:template match="*">
        <xsl:element name="{name()}">
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates select="node()"/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="htm:area|htm:base|htm:basefont|
            htm:bgsound|htm:br|htm:col|htm:frame|htm:hr|
            htm:img|htm:input|htm:isindex|htm:keygen|
            htm:link|htm:meta|htm:param">
        <!-- identity without closing tags -->
        <xsl:element name="{name()}">
            <xsl:apply-templates select="@*"/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="*[not(node())]" priority="-0.5">
        <xsl:element name="{name()}">
            <xsl:apply-templates select="@*"/>
            <xsl:text> </xsl:text>
        </xsl:element>
    </xsl:template>

    <xsl:template match="htm:script[not(node())]">
        <xsl:element name="{name()}">
            <xsl:apply-templates select="@*"/>
            <xsl:text>//</xsl:text>
        </xsl:element>
    </xsl:template>

    <xsl:template match="@*|text()">
        <xsl:copy/>
    </xsl:template>

    <xsl:template match="comment()">
        <xsl:comment xml:space="preserve">
            <xsl:value-of select="."/>
        </xsl:comment>
    </xsl:template>

    <xsl:template match="processing-instruction()">
        <xsl:processing-instruction name="{name()}">
            <xsl:value-of select="."/>
        </xsl:processing-instruction>
    </xsl:template>
</xsl:stylesheet>

解决方案:XSLT 2.0

使用 XSLT 2.0 可得到另一种方法 —xhtml— 顾名思义,它解决合理关闭空 XHTML 标签的问题。如果将名称空间应用于输入文档,必须在 xpath-default-namespace 属性中指定名称空间。清单 14 展示 xsl:output 标签上的 methodxpath-default-namespace 属性。

要使用 XSLT 2.0,使用一个支持它的 XSLT 处理器,比如 Saxon。此时,Microsoft 处理器不支持 XSLT 2.0。

清单 14. XSLT 2.0
<?xml version="1.0" ?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xhtml" 
            xpath-default-namespace="http://www.w3.org/1999/xhtml"/>

    <!-- put your templates here -->

    <!-- identity templates -->

    <xsl:template match="*">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates select="node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="@*|text()|comment()|processing-instruction()">
        <xsl:copy/>
    </xsl:template>
</xsl:stylesheet>

控制 XSLT 2.0 中的输出名称空间

要从输出中排除 XHTML 名称空间,比如在转化为另一种 XML 格式时,使用 <xsl:element> 标签,而非 <xsl:copy>,如 清单 15 所示。

清单 15. 不包括输出名称空间的 XSLT 2.0 模板
<?xml version="1.0" ?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xhtml" 
            xpath-default-namespace="http://www.w3.org/1999/xhtml"/>

    <!-- put your templates here -->

    <!-- identity templates -->

    <xsl:template match="*">
        <xsl:element name="{name()}">
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates select="node()"/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="@*|text()|comment()|processing-instruction()">
        <xsl:copy/>
    </xsl:template>
</xsl:stylesheet>

结束语

您必须合理关闭 XHTML 标签,要么使用一个单独的标签,要么使用自闭合标签,具体取决于标签名。当您通过 XSLT 转换生成 XHTML 时,用于控制如何关闭标签的方法取决于 XSLT 处理器。普遍而复杂的解决方案是编写一个序列化方法。XSLT 1.0 的其他解决方案涉及到以某种方式编码 XSL 模板。目前为止最简单的解决方案就是 XSLT 2.0,它具有对 XHTML 的本机支持。

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML, Web development
ArticleID=619297
ArticleTitle=技巧: 使用 XSLT 身份模板转换 XHTML
publish-date=12212010