【问题标题】:How to copy specific attribute using XSLT?如何使用 XSLT 复制特定属性?
【发布时间】:2011-05-09 19:01:01
【问题描述】:

对这些问题的基本性质表示歉意 - 我是 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>

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

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

这最终将由期望以属性为中心的 XML 的第三方应用程序使用。我确定我遗漏了一些非常明显的东西,但无论我用它做什么,我似乎都无法只引入 Name 属性。两个问题:

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

还有,我该如何正确处理命名空间?当我将它包含在 XSLT 中时,根据我在此处和网络上其他地方找到的示例尝试了几种方法,但我什么也没得到。

提前感谢您的任何建议。

【问题讨论】:

    标签: xml xslt


    【解决方案1】:

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

    <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 它看起来非常强大(对我来说很深奥),但试一试它会复制输出中的所有属性。 msft 来自哪里?
    • 源 XML 指定了一个命名空间:xmlns="http://schemas.microsoft.com/sharepoint/soap/directory/"。这需要在 XSLT 中加以考虑。请参阅 stylesheet 元素,了解如何为源文档中找到的命名空间分配名称(在本例中为 msft),以便您以后可以正确引用该元素。
    • 好的,但在您的解决方案中,所有属性仍会复制到输出中。
    • @empo - 不,他们不是。您使用的是 OP 的 exact 输入和我的 exact 样式表吗?我猜你没有考虑命名空间。
    • @empo - 请注意,我决定更新为使用xsl:copy,但无论哪种方式,这都不会输出所有属性。
    【解决方案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. 只有一个模板可以覆盖身份规则。它匹配名称不是“名称”的任何属性。模板的主体为空,这会导致任何匹配的属性都不会被复制。

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

    【讨论】:

    • 非常感谢!对于身份参考,这将派上用场。我是否正确假设 node()|@* 返回所有节点和所有属性?我将@*[not(name()='Name')] 读为“获取所有不具有名称名称的属性”,但实际上却相反?再次为基本问题道歉。
    • @rlg: node()|@* 是一个 XPath 表达式(在本例中为匹配模式)。它选择当前节点的任何child::node() 或任何attribute::*。这意味着:所有属性和所有元素、文本节点、处理指令和注释节点——子节点。
    • @rlg: @*[not(name()='Name')] 选择名称不是“名称”的任何属性。这意味着选择具有此匹配模式的模板来处理所有此类属性。处理为空——因此它们不会被复制到输出中。
    【解决方案3】:

    我认为如果您不在这里使用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>
    

    【讨论】:

    • 这只会为我返回最后一组,尽管我承认我不太了解 xslt,所以我可能会遗漏一些东西。我正在使用 xslt.online-toolz.com/tools/xslt-transformation.php 进行测试
    • Ops,我怪我自己……现在你有了最简单的解决方案。
    • @empo - 由于不尊重命名空间,您的第一个模板与源文档中的任何元素都不匹配。您的完整样式表示例不会产生请求的输出。
    • @Iwburk:我只是在给出提示,这不是很好和模棱两可,真的。您现在拥有完整的解决方案。
    • @Dimitre:我喜欢你的表达convoluted。很抱歉没有成为 XSLT 的大师。
    【解决方案4】:

    这似乎对我有用:

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

    【讨论】:

    • 完全错误:注意默认命名空间。此外,输出会丢失顶部元素。
    • @Dimitre - 这不是一个完整的解决方案 - 它基于 OP 的解决方案,仅替换 Group 模板。
    • 此模板不匹配任何节点——由于默认命名空间。这不可能产生非空结果。您甚至还没有测试和验证您提出的解决方案是否真的产生了想要的结果。
    • @Dimitre - 这是不公平的。我确实测试过。它在没有命名空间的情况下工作(正如 OP 所说他/她的作品“在 [...] 从原始 XML 中删除命名空间声明之后”),尽管这也不是最佳的。
    • OP 说他无法在默认命名空间的情况下进行转换 - 并不是说​​他想要没有命名空间的解决方案。
    猜你喜欢
    • 2019-07-08
    • 1970-01-01
    • 1970-01-01
    • 2015-07-31
    • 1970-01-01
    • 2018-11-23
    • 1970-01-01
    • 2020-01-07
    • 2014-02-10
    相关资源
    最近更新 更多