Нормализовать пару XML в ключ-значение в XSL 1.0 - PullRequest
0 голосов
/ 01 мая 2018

Имеется XML-файл с неопределенным количеством атрибутов «Поле». Можно ли его «нормализовать» на пары ключ-значение, используя XSL 1.0.

Может ли XML-файл выглядеть так:

<Table>
    <Record RecordNo="1" Field1="ID" Field2="Author" Field3="Title" Field4="Genre" Field5="Price" Field6="Published" />
    <Record RecordNo="2" Field1="Book001" Field2="Gambardella, Matthew" Field3="XML Developer's Guide" Field4="Computer" Field5="44.95" Field6="2000-10-01" />
    <Record RecordNo="3" Field1="Book002" Field2="Ralls, Kim" Field3="Midnight Rain" Field4="Fantasy" Field5="5.95" Field6="2000-12-16" />
    <Record RecordNo="4" Field1="Book003" Field2="Randall, Cynthia" Field3="Lover Birds" Field4="Romance" Field5="4.95" Field6="2000-09-02" />
</Table>

быть преобразованным в ...

<Table>
    <Parent ID="Book001">
        <Child Key="Author" Value="Gambardella, Matthew" />
        <Child Key="Title" Value="XML Developer's Guide" />
        <Child Key="Genre" Value="Computer" />
        <Child Key="Price" Value="44.95" />
        <Child Key="Published" Value="2000-10-01" />
    </Parent>
    <Parent ID="Book002">
        <Child Key="Author" Value="Ralls, Kim" />
        <Child Key="Title" Value="Midnight Rain" />
        <Child Key="Genre" Value="Fantasy" />
        <Child Key="Price" Value="5.95" />
        <Child Key="Published" Value="2000-12-16" />
    </Parent>
    <Parent ID="Book003">
        <Child Key="Author" Value="Randall, Cynthia" />
        <Child Key="Title" Value="Lover Bird" />
        <Child Key="Genre" Value="Romance" />
        <Child Key="Price" Value="4.95" />
        <Child Key="Published" Value="2000-09-02" />
    </Parent>   
</Table>

с использованием XSLT-1.0?

Ответы [ 2 ]

0 голосов
/ 01 мая 2018

Один из вариантов - использовать xsl:key, который выбирает все атрибуты первого Record и использует атрибуты name() для ключа.

Атрибут Field1 необходимо будет обрабатывать иначе, чем другие, поскольку он используется в Parent вместо Child.

Пример ...

Ввод XML

<Table>
    <Record RecordNo="1" Field1="ID" Field2="Author" Field3="Title" Field4="Genre" Field5="Price" Field6="Published" />
    <Record RecordNo="2" Field1="Book001" Field2="Gambardella, Matthew" Field3="XML Developer's Guide" Field4="Computer" Field5="44.95" Field6="2000-10-01" />
    <Record RecordNo="3" Field1="Book002" Field2="Ralls, Kim" Field3="Midnight Rain" Field4="Fantasy" Field5="5.95" Field6="2000-12-16" />
    <Record RecordNo="4" Field1="Book003" Field2="Randall, Cynthia" Field3="Lover Birds" Field4="Romance" Field5="4.95" Field6="2000-09-02" />
</Table>

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <!--Select all the attributes of the first Record and use the attributes 
    name for the key. See https://www.w3.org/TR/xslt-10/#key -->
  <xsl:key name="keys" match="Record[@RecordNo=1]/@*" use="name()"/>

  <xsl:template match="/Table">
    <xsl:copy>
      <!--Process Record elements, but do not process the Record
      with the attribute RecordNo value of 1. That Record is only used for
      the Key values.-->
      <xsl:apply-templates select="Record[not(@RecordNo=1)]"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Record">
    <Parent>
      <!--Process the attributes of Record, but do not process the RecordNo
      attribute.-->
      <xsl:apply-templates select="@*[not(name()='RecordNo')]">
        <!--Sort by attribute name to guarantee the order they're processed.-->
        <xsl:sort select="name()"/>
      </xsl:apply-templates>
    </Parent>
  </xsl:template>

  <xsl:template match="@Field1" priority="1">
    <!--For the Field1 attribute, create an attribute. Since we know the name of
    this attribute, we could also use key('keys','Field1') to get the name.
    Also, the curly braces ({}) is an AVT (Attribute Value Template). 
    See https://www.w3.org/TR/xslt-10/#attribute-value-templates-->
    <xsl:attribute name="{key('keys',name())}">
      <xsl:value-of select="."/>
    </xsl:attribute>
  </xsl:template>

  <xsl:template match="@*">
    <!--For any other attributes, create a Child element. They key lookup is based on the
    name of the attribute. (The second argument in key() corresponds to the "use"
    attribute of xsl:key.)-->
    <Child Key="{key('keys',name())}" Value="{.}"/>
  </xsl:template>

</xsl:stylesheet>

выход

<Table>
   <Parent ID="Book001">
      <Child Key="Author" Value="Gambardella, Matthew"/>
      <Child Key="Title" Value="XML Developer's Guide"/>
      <Child Key="Genre" Value="Computer"/>
      <Child Key="Price" Value="44.95"/>
      <Child Key="Published" Value="2000-10-01"/>
   </Parent>
   <Parent ID="Book002">
      <Child Key="Author" Value="Ralls, Kim"/>
      <Child Key="Title" Value="Midnight Rain"/>
      <Child Key="Genre" Value="Fantasy"/>
      <Child Key="Price" Value="5.95"/>
      <Child Key="Published" Value="2000-12-16"/>
   </Parent>
   <Parent ID="Book003">
      <Child Key="Author" Value="Randall, Cynthia"/>
      <Child Key="Title" Value="Lover Birds"/>
      <Child Key="Genre" Value="Romance"/>
      <Child Key="Price" Value="4.95"/>
      <Child Key="Published" Value="2000-09-02"/>
   </Parent>
</Table>

Fiddle: http://xsltfiddle.liberty -development.net / pPqsHTa / 2

0 голосов
/ 01 мая 2018

Вы можете использовать следующие два шаблона в таблице стилей.
Они используют значения первого Record, отраженного как TableHeader (th) в xsl:variable:

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

<xsl:template match="Record[@RecordNo > 1]">
    <xsl:variable name="th" select="/Table/Record[@RecordNo = 1]" />
    <Parent>
      <xsl:attribute name="{$th/@Field1}"><xsl:value-of select="@Field1" /></xsl:attribute>
      <xsl:for-each select="./@*[starts-with(local-name(),'Field')][position() > 1]">
        <xsl:sort select="local-name()" />   <!-- to process attributes in the right order -->
        <xsl:variable name="attrName" select="concat('Field',position()+1)" />
        <xsl:variable name="HeaderName" select="$th/@*[contains(local-name(),$attrName)]" />
        <Child Key="{$HeaderName}" Value="{.}" />
      </xsl:for-each>
    </Parent>
</xsl:template>

Это будет работать для произвольного количества Field* узлов.

...