如何使用XSLT复制特定属性?

时间:2011-05-09 19:01:02

标签: xml xslt

对这些问题的基本性质表示道歉 - 我对XSLT(以及Stack Overflow)也是全新的。

我需要转换Sharepoint Web服务返回的以下XML:

<GetGroupCollectionFromUser xmlns=
   "http://schemas.microsoft.com/sharepoint/soap/directory/">
   <Groups>
      <Group ID="3" Name="Group1" Description="Description" OwnerID="1" 
         OwnerIsUser="False" />
      <Group ID="15" Name="Group2" Description="Description" 
         OwnerID="12" OwnerIsUser="True" />
      <Group ID="16" Name="Group3" Description="Description" 
         OwnerID="7" OwnerIsUser="False" />
   </Groups>
</GetGroupCollectionFromUser>

进入这个:

<GetGroupCollectionFromUser xmlns=
   "http://schemas.microsoft.com/sharepoint/soap/directory/">
   <Groups>
      <Group Name="Group1" />
      <Group Name="Group2" />
      <Group Name="Group3" />
   </Groups>
</GetGroupCollectionFromUser>

基本上,我需要删除除Name之外的每个Group元素的所有属性。经过大量的研究和修补,特别是从原始XML中删除命名空间声明后,我想出了一些让我几乎完全符合我需要的东西:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml" encoding="utf-8" indent="no"/>

<xsl:template match="/GetGroupCollectionFromUser">
    <xsl:copy>                  
        <xsl:apply-templates select="Groups" />
    </xsl:copy>
</xsl:template>

<xsl:template match="Groups">
    <xsl:copy>
        <xsl:apply-templates select="Group" />
    </xsl:copy>
</xsl:template> 

<xsl:template match="Group">
    <xsl:copy>
        <xsl:apply-templates select="@Name" />
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

但是,上面给出了作为文本插入Group元素的Name属性的值,而不是Name属性,如下所示:

<GetGroupCollectionFromUser>
    <Groups>
        <Group>Group1</Group>
        <Group>Group2</Group>
        <Group>Group3</Group>
    </Groups>
</GetGroupCollectionFromUser>

这最终将由期望以属性为中心的XML的第三方应用程序使用。我确定我错过了一些令人尴尬的东西,但无论我用它做什么,我似乎都无法仅仅引入Name属性。两个问题:

如何更改XSLT以返回每个Group元素的Name属性,而不是将其值作为文本返回?

而且,如何正确处理命名空间?当我将它包含在XSLT中时,尝试基于我在这里和网上其他地方找到的示例的几种方法,我什么也得不到。

提前感谢任何建议。

4 个答案:

答案 0 :(得分:3)

处理这些情况的方法是重写身份转换:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:msft="http://schemas.microsoft.com/sharepoint/soap/directory/">
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
    </xsl:template>
    <xsl:template match="msft:Group">
        <xsl:copy>
            <xsl:apply-templates select="@Name" />
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

输出:

<GetGroupCollectionFromUser
    xmlns="http://schemas.microsoft.com/sharepoint/soap/directory/">
    <Groups>
        <Group Name="Group1" />
        <Group Name="Group2" />
        <Group Name="Group3" />
    </Groups>
</GetGroupCollectionFromUser>

请注意,输出正确命名空间。

由于其简单性和灵活性,该解决方案是优选的。除非存在指定备用行为的覆盖,否则将按原样复制所有节点和属性。

答案 1 :(得分:2)

如果你不在这里使用xsl:copy,我认为你会变得更容易。试试这个:

<xsl:template match="Groups/*">
 <xsl:element name="Group">
     <xsl:attribute name="Name">
      <xsl:value-of select="@Name" />
     </xsl:attribute>
 </xsl:element>
</xsl:template>

对第二个问题的回答是FAQ。声明命名空间并在xsl中使用声明的前缀。最终解决方案:

<xsl:stylesheet version="1.0" 
    xmlns:myns="http://schemas.microsoft.com/sharepoint/soap/director/"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 <xsl:output method="xml" encoding="utf-8" indent="no"/>

 <xsl:template match="/myns:GetGroupCollectionFromUser">
  <Groups>                 
    <xsl:apply-templates select="myns:Groups" />
  </Groups>
 </xsl:template>

 <xsl:template match="myns:Groups/*">
  <xsl:element name="Group">
     <xsl:attribute name="Name">
      <xsl:value-of select="@Name" />
     </xsl:attribute>
  </xsl:element>
 </xsl:template>
</xsl:stylesheet>

答案 2 :(得分:2)

这可能是正确产生所需结果的最短转换之一

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

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

 <xsl:template match="@*[not(name()='Name')]"/>
</xsl:stylesheet>

应用于提供的XML文档

<GetGroupCollectionFromUser xmlns=
"http://schemas.microsoft.com/sharepoint/soap/directory/">
    <Groups>
        <Group ID="3" Name="Group1" Description="Description" OwnerID="1"           OwnerIsUser="False" />
        <Group ID="15" Name="Group2" Description="Description"           OwnerID="12" OwnerIsUser="True" />
        <Group ID="16" Name="Group3" Description="Description"           OwnerID="7" OwnerIsUser="False" />
    </Groups>
</GetGroupCollectionFromUser>

产生了想要的正确结果

<GetGroupCollectionFromUser xmlns="http://schemas.microsoft.com/sharepoint/soap/directory/">
   <Groups>
      <Group Name="Group1"/>
      <Group Name="Group2"/>
      <Group Name="Group3"/>
   </Groups>
</GetGroupCollectionFromUser>

<强>解释

  1. 身份规则(模板)按“原样”复制每个节点。

  2. 只有一个模板会覆盖身份规则。它匹配名称不是“Name”的任何属性。模板的主体为空,这导致未复制任何匹配的属性。

  3. 使用和覆盖标识规则是最基本和最强大的XSLT设计模式。了解它here

答案 3 :(得分:0)

这似乎对我有用:

<xsl:template match="Group">
    <xsl:copy>
        <xsl:attribute name="Name">
            <xsl:apply-templates select="@Name" />
        </xsl:attribute>
    </xsl:copy>
</xsl:template>