使用XSL 1.0将XML规范化为键值对

时间:2018-05-01 19:04:22

标签: xml xslt xslt-1.0

给定一个具有不确定数量的“Field”属性的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 :(得分:1)

一种选择是使用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>

小提琴:http://xsltfiddle.liberty-development.net/pPqsHTa/2

答案 1 :(得分:0)

您可以在样式表中使用以下两个模板 他们使用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*个节点。