muenchian 分组从 xsl 键创建节点

muenchian grouping to create node from xsl key

使用 xsl key 解析一些独特的条目并使用 key 中的键值创建新节点。

以下xml:

<Services>
        <Service Name="Publish" TypeName="FzUDP.Publish" ProviderName="FzUDP" Position="69,102.533530201483" InitPriority="1">
          <Parameter Name="MultiCastIp" Value="224.0.0.0" />
          <Parameter Name="MultiCastPort" Value="61499" />
          <Parameter Name="Encryption" Value="0" />
          <Parameter Name="PacketFormat" Value="0" />
        </Service>
        <Service Name="Subscribe" TypeName="FzUDP.Subscribe" ProviderName="FzUDP_1" Position="547,107.533530201483" InitPriority="2">
          <Parameter Name="MultiCastIp" Value="224.0.0.0" />
          <Parameter Name="MultiCastPort" Value="61499" />
          <Parameter Name="Decryption" Value="0" />
          <Parameter Name="PacketFormat" Value="0" />
        </Service>
<Service Name="Subscribe2" TypeName="FzUDP.Subscribe" ProviderName="FzUDP_1" Position="547,107.533530201483" InitPriority="2">
          <Parameter Name="MultiCastIp" Value="224.0.0.0" />
          <Parameter Name="MultiCastPort" Value="61499" />
          <Parameter Name="Decryption" Value="0" />
          <Parameter Name="PacketFormat" Value="0" />
        </Service>
</Services>

需要转化为:

<ServiceProviders>
<ServiceProvider Name="FzUDP" TypeName="FzUDP">
    <Services>
        <Service Name="Publish" TypeName="Publish" InitPriority="1">
          <Parameter Name="MultiCastIp" Value="224.0.0.0" />
          <Parameter Name="MultiCastPort" Value="61499" />
          <Parameter Name="Encryption" Value="0" />
          <Parameter Name="PacketFormat" Value="0" />
        </Service>
        <Service Name="Subscribe" TypeName="Subscribe" InitPriority="2">
          <Parameter Name="MultiCastIp" Value="224.0.0.0" />
          <Parameter Name="MultiCastPort" Value="61499" />
          <Parameter Name="Decryption" Value="0" />
          <Parameter Name="PacketFormat" Value="0" />
        </Service>
    </Services>
</ServiceProvider>  
<ServiceProvider Name="FzUDP_1" TypeName="FzUDP">
    <Services>
        <Service Name="Subscribe2" TypeName="Subscribe" InitPriority="2">
          <Parameter Name="MultiCastIp" Value="224.0.0.0" />
          <Parameter Name="MultiCastPort" Value="61499" />
          <Parameter Name="Decryption" Value="0" />
          <Parameter Name="PacketFormat" Value="0" />
        </Service>
    </Services>
</ServiceProvider>

到目前为止我想出的xsl代码是:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>

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

<xsl:key name="uniqueProviders" match="Service[@ProviderName]" use="Service[@Name]" />


<xsl:template match="Services">
<ServiceProviders>
  <xsl:for-each select=?>


  </xsl:for-each>


</ServiceProviders>
</xsl:template>
</xsl:stylesheet>

我正在尝试在服务元素中使用 ProviderName,并将它们作为键,将服务名称作为值。但我不确定如何使用它们。另请注意:

a. Attribute Position is not required in the transformed XML.
b. The TypeName attribute of the ServiceProvider element is the first '.' split of the TypeName attribute of the Service element.

所以我认为Service元素需要在ServiceProviders之后创建,但是Parameter元素可能会被复制。我从这里去哪里?

编辑 1:我使用 XSLT 2.0 得到以下结果

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="2.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:saxon="http://saxon.sf.net/"
                extension-element-prefixes="saxon"
  xmlns:bootFile="http://tempuri.org/BootfileDefinition"
                xmlns:apps="http://tempuri.org/FZDevice">

  <xsl:output method="xml" indent="yes"/>


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



  <xsl:template match ="//apps:Services">
    <xsl:element name="ServiceProviders">
      <xsl:for-each-group select="//apps:Service" group-by="//apps:Service/@ProviderName">
        <xsl:element name="ServiceProvider">
          <xsl:attribute name="Name">
            <xsl:value-of select="current-grouping-key()"/>
          </xsl:attribute>

          <xsl:attribute name="TypeName">
            <xsl:value-of select="tokenize(current-group()[1]/@TypeName, '\.')[1]"/>
          </xsl:attribute>

          <xsl:for-each select="current-group()">
            <xsl:element name="Service">

              <xsl:attribute name="Name">
                <xsl:value-of select="@Name"/>
              </xsl:attribute>

              <xsl:attribute name="TypeName">
                <xsl:value-of select="tokenize(@TypeName, '\.')[2]"/>
              </xsl:attribute>

              <xsl:attribute name="InitPriority">
                <xsl:value-of select="@InitPriority"/>
              </xsl:attribute>

              <xsl:copy-of select="//apps:Parameter"/>

            </xsl:element>
          </xsl:for-each> 

        </xsl:element>
      </xsl:for-each-group>
     </xsl:element>
  </xsl:template>


</xsl:stylesheet>

请注意,元素名称前面有名称空间。我会在输出 XML 中去掉它们,如果有人能帮助我在 XSLT 1.0 中做同样的事情,我将不胜感激。

考虑对 Muenchian 分组的这种调整,因为您不需要身份转换,而是重写 Service 为分组键量身定制的模板。此外,要按句点拆分 @TypeName 内容,请使用 substring-beforesubstring-after 函数。并且您似乎希望 <xsl:key>@ProviderName@TypeName 的串联(在期间部分之前)。

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                              xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
    <xsl:output method="xml" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:key name="uniqueProviders" match="Service" use="concat(@ProviderName, substring-before(@TypeName, '.'))" />

    <xsl:template match="/Services">
        <ServiceProviders>
            <xsl:apply-templates select="Service"/>
        </ServiceProviders>
    </xsl:template>  

    <xsl:template match="Service[generate-id() = generate-id(key('uniqueProviders', concat(@ProviderName, substring-before(@TypeName, '.')))[1])]">
        <ServiceProvider Name="{@ProviderName}" TypeName="{substring-before(@TypeName, '.')}">
            <xsl:for-each select="key('uniqueProviders', concat(@ProviderName, substring-before(@TypeName, '.')))">
                <Service Name="{@Name}" TypeName="{substring-after(@TypeName, '.')}" InitPriority="{@InitPriority}">
                    <xsl:copy-of select="*"/>
                </Service>       
            </xsl:for-each>
        </ServiceProvider>
    </xsl:template>    

</xsl:stylesheet>

输出

<?xml version="1.0" encoding="utf-8"?>
<ServiceProviders>
  <ServiceProvider Name="FzUDP" TypeName="FzUDP">
    <Service Name="Publish" TypeName="Publish" InitPriority="1">
      <Parameter Name="MultiCastIp" Value="224.0.0.0" />
      <Parameter Name="MultiCastPort" Value="61499" />
      <Parameter Name="Encryption" Value="0" />
      <Parameter Name="PacketFormat" Value="0" />
    </Service>
  </ServiceProvider>
  <ServiceProvider Name="FzUDP_1" TypeName="FzUDP">
    <Service Name="Subscribe" TypeName="Subscribe" InitPriority="2">
      <Parameter Name="MultiCastIp" Value="224.0.0.0" />
      <Parameter Name="MultiCastPort" Value="61499" />
      <Parameter Name="Decryption" Value="0" />
      <Parameter Name="PacketFormat" Value="0" />
    </Service>
    <Service Name="Subscribe2" TypeName="Subscribe" InitPriority="2">
      <Parameter Name="MultiCastIp" Value="224.0.0.0" />
      <Parameter Name="MultiCastPort" Value="61499" />
      <Parameter Name="Decryption" Value="0" />
      <Parameter Name="PacketFormat" Value="0" />
    </Service>
  </ServiceProvider>
</ServiceProviders>