使用xslt2将结构化文本转换为XML

时间:2013-08-28 16:28:17

标签: xml xslt xslt-2.0

我正在尝试使用xslt处理一些旧数据文件,它们在对象描述语言(ODL)中包含一个大型文本元素,它具有类似XML的结构。我试图使用analyze-string将这些结构转换为xml。我可以匹配字符串并采取适当的措施,但我无法弄清楚如何翻译结构...

输入:

<?xml version="1.0" encoding="UTF-8"?>
<h4:HDF4map  version="1.0.1" 
    xsi:schemaLocation="http://www.hdfgroup.org/HDF4/XML/schema/HDF4map/1.0.1/HDF4map.xsd" 
    xmlns:h4="http://www.hdfgroup.org/HDF4/XML/schema/HDF4map/1.0.1" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <h4:HDF4FileContents>
        <h4:FileAttribute name="StructMetadata" origin="File Attribute: Arrays" combinesMultipleAttributes="true" id="ID_FA2">
            <h4:stringValue>
    GROUP=SwathStructure
        GROUP=SWATH_1
            SwathName="L2_Support_atmospheric&amp;surface_product"
            GROUP=Dimension
                OBJECT=Dimension_1
                    DimensionName="GeoXTrack"
                    Size=30
                END_OBJECT=Dimension_1
                OBJECT=Dimension_2
                    DimensionName="GeoTrack"
                    Size=45
                END_OBJECT=Dimension_2
            END_GROUP=Dimension
            GROUP=GeoField
                OBJECT=GeoField_1
                    GeoFieldName="Latitude"
                    DataType=DFNT_FLOAT64
                    DimList=("GeoTrack","GeoXTrack")
                END_OBJECT=GeoField_1
                OBJECT=GeoField_2
                    GeoFieldName="Longitude"
                    DataType=DFNT_FLOAT64
                    DimList=("GeoTrack","GeoXTrack")
                END_OBJECT=GeoField_2
                OBJECT=GeoField_3
                    GeoFieldName="Time"
                    DataType=DFNT_FLOAT64
                    DimList=("GeoTrack","GeoXTrack")
                END_OBJECT=GeoField_3
            END_GROUP=GeoField
        END_GROUP=SWATH_1
    END_GROUP=SwathStructure
        </h4:stringValue>
        </h4:FileAttribute>
    </h4:HDF4FileContents>
</h4:HDF4map>

我想将FileAttribute / stringValue转换为:

<root xmlns:h4="http://www.hdfgroup.org/HDF4/XML/schema/HDF4map/1.0.1">
   <group name="SwathStructure">
      <group name="SWATH_1">
         <SwathName>"L2_Support_atmospheric&amp;surface_product"</SwathName>
         <group name="Dimension">
            <Dimension_1>
               <DimensionName>"GeoXTrack"</DimensionName>
               <Size>30</Size>
            </Dimension_1>
            <Dimension_2>
               <DimensionName>"GeoTrack"</DimensionName>
               <Size>45</Size>
            </Dimension_2>
         </group>
         <group name="GeoField">
            <GeoField_1>
               <GeoFieldName>"Latitude"</GeoFieldName>
               <DataType>DFNT_FLOAT64</DataType>
               <DimList>("GeoTrack","GeoXTrack")</DimList>
            </GeoField_1>
            <GeoField_2>
               <GeoFieldName>"Longitude"</GeoFieldName>
               <DataType>DFNT_FLOAT64</DataType>
               <DimList>("GeoTrack","GeoXTrack")</DimList>
            </GeoField_2>
            <GeoField_3>
               <GeoFieldName>"Time"</GeoFieldName>
               <DataType>DFNT_FLOAT64</DataType>
               <DimList>("GeoTrack","GeoXTrack")</DimList>
            </GeoField_3>
         </group>
      </group>
   </group>
</root>

这是我到目前为止所做的:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xmlns:h4="http://www.hdfgroup.org/HDF4/XML/schema/HDF4map/1.0.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:xlink="http://www.w3.org/1999/xlink" exclude-result-prefixes="xsi xlink">

  <!-- Parameters to identify files to process -->
  <xsl:param name="recordSetPath"/>
  <xsl:param name="fileNamePattern"/>
  <xsl:output method="xml" indent="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="/">
    <xsl:element name="root">
      <xsl:namespace name="h4" select="'http://www.hdfgroup.org/HDF4/XML/schema/HDF4map/1.0.1'"/>
      <xsl:variable name="xmlFilesSelect" select="concat($recordSetPath, '?select=',$fileNamePattern)"/>
      <xsl:for-each select="collection(iri-to-uri($xmlFilesSelect))">
        <xsl:apply-templates/>
      </xsl:for-each>
    </xsl:element>
  </xsl:template>
  <xsl:template match="h4:FileAttribute[@name='StructMetadata']">
    <xsl:variable name="elementValue" select="h4:stringValue"/>
    <xsl:analyze-string select="$elementValue" regex="(.*)=(.*)">
      <xsl:matching-substring>
        <xsl:variable name="objectName" select="normalize-space(regex-group(1))"/>
        <xsl:variable name="objectValue" select="normalize-space(regex-group(2))"/>
        <xsl:choose>
          <xsl:when test="$objectName='GROUP'">
            <xsl:element name="group">
              <xsl:attribute name="name" select="$objectValue"/>
            </xsl:element>
          </xsl:when>
          <xsl:when test="contains($objectName,'END-GROUP')">
            <xsl:value-of select="'&lt;/group>'"/>
          </xsl:when>
          <xsl:when test="$objectName='OBJECT'">
            <xsl:element name="{$objectValue}"/>
          </xsl:when>
          <xsl:when test="contains($objectName,'END-OBJECT')">
            <xsl:value-of select="'&lt;/group>'"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:element name="{$objectName}">
              <xsl:value-of select="$objectValue"/>
            </xsl:element>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:matching-substring>
    </xsl:analyze-string>
  </xsl:template>
  <xsl:template match="text()"/>
</xsl:stylesheet>

2 个答案:

答案 0 :(得分:1)

这还不是一个完整的解决方案。但我首先要从您的输入中创建简单的XML。然后处理新的XML树。这应该可以使用apply-templates,尽管您可能需要使用pull处理方法而不是push(即,实际上可能不适用于apply-templates ...)。

这些模板构成了一个简单的数据XML,使每个行的等号前面的元素名称和元素的文本节点之后的东西。

 <xsl:template match="h4:stringValue">
    <xsl:variable name="newRoot">
    <xsl:for-each select="tokenize(.,'&#xA;')">
                <xsl:variable name="newElementName" select="substring-before(.,'=')"/>
            <xsl:if test="$test ne '' and $test ne ' '"    >
                <xsl:element name="{$newElementName}">
                    <xsl:value-of select="substring-after(.,'=')"/>
                </xsl:element>
            </xsl:if>
    </xsl:for-each>     
    </xsl:variable>
    <xsl:sequence select="$newRoot"/>
    <xsl:apply-templates select="$newRoot" mode="process"/>

</xsl:template>
<xsl:template match="text()"/>

答案 1 :(得分:1)

数据需要超级一致才能实现,但我要做的是创建一个中间XML结构(如user5923提出的那样),然后对其进行处理。

此解决方案使用了大量disable-output-escaping,并且很容易导致输出格式不正确。它适用于此示例,但如果您使用永久解决方案,请谨慎使用。

XML输入

<h4:HDF4map  version="1.0.1" 
    xsi:schemaLocation="http://www.hdfgroup.org/HDF4/XML/schema/HDF4map/1.0.1/HDF4map.xsd" 
    xmlns:h4="http://www.hdfgroup.org/HDF4/XML/schema/HDF4map/1.0.1" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <h4:HDF4FileContents>
        <h4:FileAttribute name="StructMetadata" origin="File Attribute: Arrays" combinesMultipleAttributes="true" id="ID_FA2">
            <h4:stringValue>
                GROUP=SwathStructure
                GROUP=SWATH_1
                SwathName="L2_Support_atmospheric&amp;surface_product"
                GROUP=Dimension
                OBJECT=Dimension_1
                DimensionName="GeoXTrack"
                Size=30
                END_OBJECT=Dimension_1
                OBJECT=Dimension_2
                DimensionName="GeoTrack"
                Size=45
                END_OBJECT=Dimension_2
                END_GROUP=Dimension
                GROUP=GeoField
                OBJECT=GeoField_1
                GeoFieldName="Latitude"
                DataType=DFNT_FLOAT64
                DimList=("GeoTrack","GeoXTrack")
                END_OBJECT=GeoField_1
                OBJECT=GeoField_2
                GeoFieldName="Longitude"
                DataType=DFNT_FLOAT64
                DimList=("GeoTrack","GeoXTrack")
                END_OBJECT=GeoField_2
                OBJECT=GeoField_3
                GeoFieldName="Time"
                DataType=DFNT_FLOAT64
                DimList=("GeoTrack","GeoXTrack")
                END_OBJECT=GeoField_3
                END_GROUP=GeoField
                END_GROUP=SWATH_1
                END_GROUP=SwathStructure
            </h4:stringValue>
        </h4:FileAttribute>
    </h4:HDF4FileContents>
</h4:HDF4map>

XSLT 2.0

<xsl:stylesheet version="2.0" xmlns:h4="http://www.hdfgroup.org/HDF4/XML/schema/HDF4map/1.0.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/h4:HDF4map/h4:HDF4FileContents/h4:FileAttribute[@name='StructMetadata']/h4:stringValue">
        <xsl:variable name="pass1">
            <xsl:analyze-string select="." regex="([^=\s]+)=([^=\s]+)">
                <xsl:matching-substring>
                    <tag name="{regex-group(1)}" value="{regex-group(2)}"/>
                </xsl:matching-substring>
                <xsl:non-matching-substring/>
            </xsl:analyze-string>           
        </xsl:variable>
        <root>
            <xsl:for-each select="$pass1/tag">
                <xsl:choose>
                    <xsl:when test="@name='END_OBJECT'">
                        <xsl:value-of select="concat('&lt;/',@value,'>&#xA;')" disable-output-escaping="yes"/>
                    </xsl:when>
                    <xsl:when test="starts-with(@name,'END_')">
                        <xsl:value-of select="concat('&lt;/',lower-case(substring-after(@name,'END_')),'>&#xA;')" disable-output-escaping="yes"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:choose>
                            <xsl:when test="@name='OBJECT'">
                                <xsl:value-of select="concat('&lt;',@value,'>&#xA;')" disable-output-escaping="yes"/>
                            </xsl:when>                     
                            <xsl:when test="following-sibling::*[@name=concat('END_',current()/@name)]">
                                <xsl:value-of select="concat('&lt;',lower-case(@name),' name=&quot;')" disable-output-escaping="yes"/>
                                <xsl:value-of select="replace(@value,'^&quot;|&quot;$','')"/>
                                <xsl:value-of select="'&quot;>&#xA;'" disable-output-escaping="yes"/>                               
                            </xsl:when>
                            <xsl:otherwise>
                                <xsl:value-of select="concat('&lt;',@name,'>')" disable-output-escaping="yes"/>
                                <xsl:value-of select="@value"/>
                                <xsl:value-of select="concat('&lt;/',@name,'>&#xA;')" disable-output-escaping="yes"/>
                            </xsl:otherwise>
                        </xsl:choose>                       
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each>         
        </root>
    </xsl:template>

</xsl:stylesheet>

XML输出(我对输出进行了格式化以便于阅读。有一些&#xA;用于输出中断以帮助格式化,但这些可以删除。)

<root xmlns:h4="http://www.hdfgroup.org/HDF4/XML/schema/HDF4map/1.0.1">
    <group name="SwathStructure">
        <group name="SWATH_1">
            <SwathName>"L2_Support_atmospheric&amp;surface_product"</SwathName>
            <group name="Dimension">
                <Dimension_1>
                    <DimensionName>"GeoXTrack"</DimensionName>
                    <Size>30</Size>
                </Dimension_1>
                <Dimension_2>
                    <DimensionName>"GeoTrack"</DimensionName>
                    <Size>45</Size>
                </Dimension_2>
            </group>
            <group name="GeoField">
                <GeoField_1>
                    <GeoFieldName>"Latitude"</GeoFieldName>
                    <DataType>DFNT_FLOAT64</DataType>
                    <DimList>("GeoTrack","GeoXTrack")</DimList>
                </GeoField_1>
                <GeoField_2>
                    <GeoFieldName>"Longitude"</GeoFieldName>
                    <DataType>DFNT_FLOAT64</DataType>
                    <DimList>("GeoTrack","GeoXTrack")</DimList>
                </GeoField_2>
                <GeoField_3>
                    <GeoFieldName>"Time"</GeoFieldName>
                    <DataType>DFNT_FLOAT64</DataType>
                    <DimList>("GeoTrack","GeoXTrack")</DimList>
                </GeoField_3>
            </group>
        </group>
    </group>
</root>