XSLT 分组和转换

XSLT grouping and transformation

我必须通过将相同的节点分组并将它们放入相同的父节点来转换 XML 文件。 示例文件如下所示:

<cars>
  <car>
    <seller>A</seller>
    <make>Ford</make>
    <model>Mondeo</model>
    <type>Hatchback</type>
  </car>
  <car>
    <seller>A</seller>
    <make>Ford</make>
    <model>Mondeo</model>
    <type>Sedan</type>
  </car>
  <car>
    <seller>A</seller>
    <make>Ford</make>
    <model>Mondeo</model>
    <type>Station wagon</type>
  </car>
  <car>
    <seller>A</seller>
    <make>Citroen</make>
    <model>C5</model>
    <type>Sedan</type>
  </car>
  <car>
    <seller>A</seller>
    <make>Citroen</make>
    <model>C4</model>
    <type>Hatchback</type>
  </car>
  <car>
    <seller>A</seller>
    <make>Citroen</make>
    <model>C3</model>
    <type>Hatchback</type>
  </car>
  <car>
    <seller>A</seller>
    <make>Opel</make>
    <model>Corsa</model>
    <type>Hatchback</type>
  </car>
  <car>
    <seller>A</seller>
    <make>Opel</make>
    <model>Vectra</model>
    <type>Sedan</type>
  </car>
  <car>
    <seller>A</seller>
    <make>Opel</make>
    <model>Vectra</model>
    <type>Station wagon</type>
  </car>
</cars>

此文件可以按不同顺序包含更多品牌、型号和类型,但始终只有一个卖家。我必须让它看起来像这样:

<cars>
  <seller>A</seller>
  <make name="Ford">
    <model name="Mondeo" type="Hatchback"/>
    <model name="Mondeo" type="Sedan"/>
    <model name="Mondeo" type="Station wagon"/>
  </make>
  <make name="Citroen">
    <model name="C5" type="Sedan"/>
    <model name="C4" type="Hatchback"/>
    <model name="C3" type="Hatchback"/>
  </make>
  <make name="Opel">
    <model name="Corsa" type="Hatchback"/>
    <model name="Vectra" type="Sedan"/>
    <model name="Vectra" type="Station wagon"/>
  </make>
</cars>

在我尝试通过检查当前模型是否与前一个兄弟模型相同之前,我无法做到这一点是“有弹性的”并且可以为每个品牌的每个模型提供更多类型。 我知道如何将一个汽车节点转换为所需的格式:

<?xml version="1.0" encoding="UTF-8"?>
<localEntry key="CarsXSL" xmlns="http://ws.apache.org/ns/synapse">
<xsl:stylesheet version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xsl:output method="xml" omit-xml-declaration="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/">
      <xsl:element name="cars">
        <xsl:apply-templates/>
      </xsl:element>
    </xsl:template>
    <xsl:template match="cars">
        <xsl:element name="seller">
          <xsl:value-of select="/cars/car[1]/seller/text()"/>
        </xsl:element>
        <xsl:element name="make">
          <xsl:attribute name="name">
            <xsl:value-of select="/cars/car/make/text()"/>
          </xsl:attribute>
          <xsl:element name="model">
            <xsl:attribute name="name">
              <xsl:value-of select="/cars/car/make/model/text()"/>
            </xsl:attribute>
            <xsl:attribute name="type">
              <xsl:value-of select="/cars/car/make/model/type/text()"/>
            </xsl:attribute>
          </xsl:element>
        </xsl:element>
    </xsl:template>
  </xsl:stylesheet>
</localEntry>

但我不知道如何按品牌和型号对其进行分组,然后如何将它们放入相同的父节点中。 有可能吗?

试试这个:(根据@michael-hor257k的建议更新)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="1.0">
   <xsl:key name="card" match="car" use="make"/>
   <xsl:key name="modeld" match="car" use="model"/>
   <xsl:key name="types" match="car" use="concat(make, '_', model)"/>
    
<xsl:output indent="yes"/>
<xsl:template match="cars">
    <xsl:copy>
        <xsl:for-each select="car[generate-id() = generate-id(key('card', make)[1])]">
           <xsl:copy>
               <make name="{make}">
                   <xsl:for-each select="key('card', make)[generate-id() =
                       generate-id(key('modeld', model)[1])]">
                       <model name="{model}">
                           <xsl:copy-of select="key('types', concat(make, '_', model))/type"/>
                       </model>
                   </xsl:for-each>
               </make>
           </xsl:copy>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>
    
</xsl:stylesheet>

请参阅 https://xsltfiddle.liberty-development.net/6q1S8AD/1

处的转换

针对您更新后的问题

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="1.0">
   <xsl:key name="card" match="car" use="make"/>
    <xsl:key name="types" match="car" use="concat(make, '_', model, '_', type)"/>

    <xsl:output indent="yes"/>
    <xsl:template match="cars">
        <xsl:copy>
            <seller>
                <xsl:value-of select="//seller[1]"/>
            </seller>
            <car>
                <xsl:for-each select="//car[generate-id() = generate-id(key('card', make)[1])]">
                    <make name="{make}">
                        <xsl:for-each select="key('card', make)[generate-id() = generate-id(key('types', concat(make, '_', model, '_', type))[1])]">
                            <model name="{model}" type="{type}"/>
                        </xsl:for-each>
                    </make>
                </xsl:for-each>
            </car>
        </xsl:copy>
    </xsl:template>
    
</xsl:stylesheet>

请参阅 https://xsltfiddle.liberty-development.net/6q1S8AD/2

处的转换