给定一个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?
一种选择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
您可以在样式表中使用以下两个模板。
它们使用xsl:variable
中反映为 TableHeader(th
( 的第一个Record
的值:
<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*
节点。