XSLT 将命名空间前缀添加到元素最佳实践

Posted

技术标签:

【中文标题】XSLT 将命名空间前缀添加到元素最佳实践【英文标题】:XSLT Add namespace prefix to element best practice 【发布时间】:2014-04-17 08:33:00 【问题描述】:

出于验证目的,我需要为我的转换结果文件的每个元素添加命名空间前缀。 我在下面写了转换,但我认为这不是做我想做的最好的方法,而且无论如何它不能 100% 工作......

在我的源文件中有没有前缀的元素,我需要添加默认命名空间的前缀gmd。但也有一些其他的元素已经指定了前缀,因为它们引用了其他命名空间,例如gcogml,这些必须被维护。

在极少数情况下,我的输入文件可能已经设置了所有命名空间前缀。所以我只想继续其余的转换(为简单起见,我在这里只包含了一个其他模板)而不添加任何内容。

我的转换有效,但是:

    在其余的转换中,我需要操作一些元素来更改子元素的顺序、名称等...以及与另一个模板匹配的那些元素,似乎与标识模板不匹配,所以我得到它们没有前缀。 我想知道如何改进我的代码

源文件

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="c:\ISO19139_rve.xsl"?>
<MD_Metadata xmlns="http://www.isotc211.org/schemas/2005/gmd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gco="http://www.isotc211.org/schemas/2005/gco" xmlns:gml="http://www.opengis.net/gml" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.isotc211.org/schemas/2005/gmd/gmd.xsd">
  <fileIdentifier>
    <gco:CharacterString>b0101011_Vincolo</gco:CharacterString>
  </fileIdentifier>
  <language>
    <gco:CharacterString>IT</gco:CharacterString>
  </language>
  <contact>
    <CI_ResponsibleParty>
      <organizationName>
        <gco:CharacterString>Comune di Conselve (capofila PATI)</gco:CharacterString>
      </organizationName>
      <role>
        <CI_RoleCode codeList="./resource/codeList.xml#CI_RoleCode" codeListValue="Autore">Autore</CI_RoleCode>
      </role>
      <contactInfo>
        <CI_Contact>
          <onlineResource>
            <CI_OnlineResource>
              <linkage>
                <URL>http://www.comune.conselve.it</URL>
              </linkage>
            </CI_OnlineResource>
          </onlineResource>
          <phone>
            <CI_Telephone>
              <voice>
                <gco:CharacterString>0499596511</gco:CharacterString>
              </voice>
            </CI_Telephone>
          </phone>
        </CI_Contact>
      </contactInfo>
      <temporalElement>
        <EX_TemporalExtent>
          <extent>
            <gml:TimePeriod gml:id="tp1">
              <gml:begin>
                <gml:TimeIstant gml:id="ti1">
                  <gml:timePosition>2007-12-01</gml:timePosition>
                </gml:TimeIstant>
              </gml:begin>
              <gml:end>
                <gml:TimeIstant gml:id="ti2">
                  <gml:timePosition>2010-01-01</gml:timePosition>
                </gml:TimeIstant>
              </gml:end>
            </gml:TimePeriod>
          </extent>
        </EX_TemporalExtent>
      </temporalElement>
    </CI_ResponsibleParty>
  </contact>
</MD_Metadata>

XSL 转换

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:gml="http://www.opengis.net/gml"
    xmlns:gco="http://www.isotc211.org/schemas/2005/gco"
    xmlns:gmd="http://www.isotc211.org/schemas/2005/gmd"
    >

    <xsl:output indent="yes" encoding="UTF-8"/>
    <xsl:strip-space elements="*"/>

    <!-- default: identity template -->
    <xsl:template match="node() | @*">
        <xsl:choose>
            <xsl:when test="namespace-uri() eq 'http://www.isotc211.org/schemas/2005/gmd'">
                <xsl:element name="gmd:name()" namespace="http://www.isotc211.org/schemas/2005/gmd">
                    <xsl:copy-of select="namespace::*"/>
                    <xsl:apply-templates select="node() | @*"/>
                </xsl:element>    
            </xsl:when>
            <xsl:otherwise>
                <xsl:copy>
                    <xsl:apply-templates select="node() | @*" />
                </xsl:copy>
            </xsl:otherwise>            
        </xsl:choose>
    </xsl:template>

    <!-- override: <CI_Contact>, reorder -->
    <xsl:template match="gmd:CI_Contact">
        <xsl:copy>
            <xsl:apply-templates select="@*" />
            <xsl:apply-templates select="gmd:phone" />
            <xsl:apply-templates select="gmd:address" />

            <xsl:if test="not(gmd:address)">
                <gmd:address>
                    <gmd:CI_Address>
                        <gmd:electronicMailAddress>
                            <gco:CharacterString/>
                        </gmd:electronicMailAddress>
                    </gmd:CI_Address>
                </gmd:address>
            </xsl:if>

            <xsl:copy-of select="gmd:onlineResource" />
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

我的实际结果

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="c:\ISO19139_rve.xsl"?>
<gmd:MD_Metadata xmlns="http://www.isotc211.org/schemas/2005/gmd"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xmlns:gco="http://www.isotc211.org/schemas/2005/gco"
                 xmlns:gml="http://www.opengis.net/gml"
                 xmlns:xlink="http://www.w3.org/1999/xlink"
                 xmlns:gmd="http://www.isotc211.org/schemas/2005/gmd"
                 xsi:schemaLocation="http://www.isotc211.org/schemas/2005/gmd/gmd.xsd">
   <gmd:fileIdentifier>
      <gco:CharacterString>b0101011_Vincolo</gco:CharacterString>
   </gmd:fileIdentifier>
   <gmd:language>
      <gco:CharacterString>IT</gco:CharacterString>
   </gmd:language>
   <gmd:contact>
      <gmd:CI_ResponsibleParty>
         <gmd:organizationName>
            <gco:CharacterString>Comune di Conselve (capofila PATI)</gco:CharacterString>
         </gmd:organizationName>
         <gmd:role>
            <gmd:CI_RoleCode codeList="./resource/codeList.xml#CI_RoleCode" codeListValue="Autore">Autore</gmd:CI_RoleCode>
         </gmd:role>
         <gmd:contactInfo>
            <CI_Contact>
               <gmd:phone>
                  <gmd:CI_Telephone>
                     <gmd:voice>
                        <gco:CharacterString>0499596511</gco:CharacterString>
                     </gmd:voice>
                  </gmd:CI_Telephone>
               </gmd:phone>
               <gmd:address>
                  <gmd:CI_Address>
                     <gmd:electronicMailAddress>
                        <gco:CharacterString/>
                     </gmd:electronicMailAddress>
                  </gmd:CI_Address>
               </gmd:address>
               <onlineResource>
                  <CI_OnlineResource>
                     <linkage>
                        <URL>http://www.comune.conselve.it</URL>
                     </linkage>
                  </CI_OnlineResource>
               </onlineResource>
            </CI_Contact>
         </gmd:contactInfo>
         <gmd:temporalElement>
            <gmd:EX_TemporalExtent>
               <gmd:extent>
                  <gml:TimePeriod gml:id="tp1">
                     <gml:begin>
                        <gml:TimeIstant gml:id="ti1">
                           <gml:timePosition>2007-12-01</gml:timePosition>
                        </gml:TimeIstant>
                     </gml:begin>
                     <gml:end>
                        <gml:TimeIstant gml:id="ti2">
                           <gml:timePosition>2010-01-01</gml:timePosition>
                        </gml:TimeIstant>
                     </gml:end>
                  </gml:TimePeriod>
               </gmd:extent>
            </gmd:EX_TemporalExtent>
         </gmd:temporalElement>
      </gmd:CI_ResponsibleParty>
   </gmd:contact>
</gmd:MD_Metadata>

如您所见,转换仅适用于其他模板不匹配的元素。查看&lt;CI_Contact&gt;&lt;onlineResource&gt;&lt;CI_OnlineResource&gt; 等的结果。

【问题讨论】:

当您说“出于验证目的,我需要为转换结果文件的每个元素添加命名空间前缀。”时,是什么样的验证?基于正常模式的验证检查节点的命名空间,但不检查前缀,因为它们无关紧要,元素 &lt;foo xmlns="http://example.com/"&gt;...&lt;/foo&gt;&lt;pf:foo xmlns:pf="http://example.com/"&gt;...&lt;/foo&gt; 一样有效。 This 是 INSPIRE 标准验证。还有 GeoNetwork 元数据导入工具。 【参考方案1】:

我会写

<xsl:template match="node() | @*">
    <xsl:choose>
        <xsl:when test="namespace-uri() eq 'http://www.isotc211.org/schemas/2005/gmd'">
            <xsl:element name="gmd:name()" namespace="http://www.isotc211.org/schemas/2005/gmd">
                <xsl:copy-of select="namespace::*"/>
                <xsl:apply-templates select="node() | @*"/>
            </xsl:element>    
        </xsl:when>
        <xsl:otherwise>
            <xsl:copy>
                <xsl:apply-templates select="node() | @*" />
            </xsl:copy>
        </xsl:otherwise>            
    </xsl:choose>
</xsl:template>

作为两个模板

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

<xsl:template match="gmd:*">
    <xsl:element name="gmd:local-name()" namespace="http://www.isotc211.org/schemas/2005/gmd">
                <xsl:copy-of select="namespace::*"/>
                <xsl:apply-templates select="node() | @*"/>
     </xsl:element>    

</xsl:template>

当然,如果您有其他模板匹配和转换 http://www.isotc211.org/schemas/2005/gmd 命名空间中的元素,那么您需要确保在它们中也完成前缀更改,例如

<!-- override: <CI_Contact>, reorder -->
<xsl:template match="gmd:CI_Contact">
    <xsl:element name="gmd:local-name()" namespace="http://www.isotc211.org/schemas/2005/gmd">
        <xsl:apply-templates select="@*" />
        <xsl:apply-templates select="gmd:phone" />
        <xsl:apply-templates select="gmd:address" />

        <xsl:if test="not(gmd:address)">
            <gmd:address>
                <gmd:CI_Address>
                    <gmd:electronicMailAddress>
                        <gco:CharacterString/>
                    </gmd:electronicMailAddress>
                </gmd:CI_Address>
            </gmd:address>
        </xsl:if>

        <xsl:copy-of select="gmd:onlineResource" />
    </xsl:element>
</xsl:template>

【讨论】:

您的解决方案在此示例 XML 文件上运行良好,但是将其放在我的原始转换中会得到一些元素,其中声明了 all namespaces ... 所以,我的另一个解决方案found 是替换每个模板上的根元素。因此,而不是:&lt;xsl:template match="gmd:CI_Contact"&gt; &lt;xsl:element name="gmd:local-name()" namespace="http://www.isotc211.org/schemas/2005/gmd"&gt; &lt;xsl:apply-templates select="@*" /&gt;... 如您所写,我使用了:&lt;xsl:template match="gmd:CI_Contact"&gt; &lt;gmd:CI_Contact&gt; &lt;xsl:apply-templates select="@*" /&gt;...

以上是关于XSLT 将命名空间前缀添加到元素最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

枚举类型的命名空间 - 最佳实践

JQuery 命名空间的最佳实践 + 通用实用程序函数

xsd:any 元素的命名空间前缀并使用 XSLT 添加命名空间前缀

最佳实践:C# 扩展方法命名空间和推广扩展方法

将命名空间添加到根元素

如何使用 XSLT 替换命名空间中的元素?