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

发布于 2024-11-06 17:36:57 字数 2322 浏览 1 评论 0原文

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

基本上,我需要删除每个 Group 元素除 Name 之外的所有属性。经过大量研究和修改,特别是从原始 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 属性,而是Name 属性作为文本插入到 Group 元素中,如下所示:

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

这最终将由需要以属性为中心的 XML 的第三方应用程序使用。我确信我错过了一些非常明显的东西,但无论我用它做什么,我似乎都无法只提取 Name 属性。两个问题:

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

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

预先感谢您的任何建议。

Apologies for the basic nature of these questions - I'm brand new to XSLT (and to Stack Overflow too).

I need to transform the following XML being returned by a Sharepoint Web service:

<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>

into this:

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

Basically, I need to drop all the attributes for each Group element except Name. After a lot of research and tinkering, and notably dropping the namespace declaration from the original XML, I've come up with something that gets me almost exactly what I need:

<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>

However, instead of a Name attribute, the above gives me the value of the Name attribute inserted as text into the Group element, like so:

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

This will ultimately be consumed by a third-party application that's expecting attribute-centric XML. I'm sure I'm missing something embarrassingly obvious, but no matter what I do with it I can't seem to pull in just the Name attribute. Two questions:

How do I alter the XSLT to return a Name attribute for each Group element, instead of its value as text?

And, how do I handle the namespace properly? When I've included it in the XSLT, trying several methods based on examples I've found here and elsewhere on the web, I get nothing at all back.

Thanks in advance for any advice.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

凡间太子 2024-11-13 17:36:57

处理这些情况的方法是覆盖身份转换:

<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>

请注意,输出已正确命名。

该解决方案因其简单性和灵活性而受到青睐。所有节点和属性均按原样复制,除非存在指定替代行为的覆盖。

The way to handle these situations is by overriding the Identity Transform:

<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>

Output:

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

Note that the output is properly namespaced.

This solution is preferred because of its simplicity and flexibility. All nodes and attributes are copied as-is, unless an override exists that specifies an alternate behavior.

尤怨 2024-11-13 17:36:57

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

<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”的任何属性。模板正文为空,这会导致任何匹配的属性都不会被复制。

使用和覆盖身份规则是最基本、最强大的 XSLT 设计模式。请此处了解相关内容。

This is possibly one of the shortest transformations that correctly produces the wanted result:

<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>

When applied on the provided XML document:

<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>

the wanted, correct result is produced:

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

Explanation:

  1. The identity rule (template) copies every node "as-is".

  2. There is just one template that overrides the identity rule. It matches any attribute whose name is not "Name". The body of the template is empty and this results in any matched attribute not copied.

Using and overriding the identity rule is the most fundamental and powerful XSLT design pattern. Read about it here.

梦回梦里 2024-11-13 17:36:57

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

第二个问题的答案是常见问题解答。声明命名空间并在 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>

I think that it becomes easier for you if you don't use xsl:copy here. Try this:

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

Answer to second question is a FAQ. Declare the namespace and use the declared prefix in your xsl. Final solution:

<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>
囍孤女 2024-11-13 17:36:57

这似乎对我有用:

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

This seems to work for me:

<xsl:template match="Group">
    <xsl:copy>
        <xsl:attribute name="Name">
            <xsl:apply-templates select="@Name" />
        </xsl:attribute>
    </xsl:copy>
</xsl:template>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文