muenchian группировка для создания узла из ключа xsl

Использование ключа xsl для анализа некоторых уникальных записей и создания новых узлов со значениями ключа в ключе.

Следующий 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 внутри элемента Service и поместить их в качестве ключа, а имя службы в качестве значений. Но я не уверен, как их использовать. Также обратите внимание, что:

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.

1 ответ

Решение

Рассмотрите эту настройку Muenchian Grouping, так как вам не нужно Identity Transform, но переписать шаблон службы, адаптированный к сгруппированному ключу. Кроме того, чтобы разделить @TypeName содержание по периодам, используйте substring-before а также substring-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>
Другие вопросы по тегам