1

次の xml コードの処理に問題があります。

<?xml version="1.0" encoding="UTF-8"?>
<searchresult>
  <head>
    <heading>
      <title>Column1</title>
      <dataType>TEXT</dataType>
    </heading>
    <heading>
      <title>Column2</title>
      <dataType>DATE</dataType>
      <dataFormat>SHORT_DATE</dataFormat>
    </heading>
  </head>
  <data>
    <row>
      <column>
        <value>Hello</value>
      <column>
      <column>
        <value>2012-07-12</value>
      <column>
    </row>
    <row>
      <column>
        <value>Good bye</value>
      <column>
      <column>
        <value>2012-07-13</value>
      <column>
    </row>
  </data>
</searchresult>

この xml を EXCEL 互換ファイルに変換する必要があります。それ)

問題は、head/heading 要素 dataType + dataFormat (利用可能な場合) からの情報を行/列/値に適用する方法がわからないことです。これは、Excel がセル内にあるデータ型を認識するのに役立ちます。順序を維持する必要があることは明らかです。列とそのメタデータの数は動的であり、各 XML は異なる場合があります。

私はこのようなものを取得する必要があります:

<?xml version="1.0" encoding="ISO-8859-1"?>
<Workbook --several namespaces here-->

<Worksheet ss:Name="SearchResult">
  <Table x:FullRows="1" x:FullColumns="1">
    <Row ss:Height="12.75">
      <Cell>
        <Data ss:Type="String">Column1</Data>
      </Cell>
      <Cell>
        <Data ss:Type="String">Column2</Data>
      </Cell>
    </Row>
    <Row ss:Height="12.75">
      <Cell>
        <Data ss:Type="String">Hello : TEXT</Data>
      </Cell>
      <Cell>
        <Data ss:Type="Date">2012-07-12 : DATE - SHORT_DATE</Data>
      </Cell>
    </Row>
    <Row ss:Height="12.75">
      <Cell>
        <Data ss:Type="String">Good bye : TEXT</Data>
      </Cell>
      <Cell>
        <Data ss:Type="Date">2012-07-12 : DATE - SHORT_DATE</Data>
      </Cell>
    </Row>
  </Table>
</Worksheet>    
</Workbook>

便利で機能するものを数回作成しようとしましたが、すべての試みが失敗しました。現在のバージョンはこちら:

  <xsl:template match="searchresult">
    <Worksheet>
      --some unimportant script--
      <Table x:FullColumns="1" x:FullRows="1">
        <xsl:apply-templates select="head" />
        <xsl:apply-templates select="elements/row"/>
      </Table>
    </Worksheet>
  </xsl:template>

  <xsl:template match="head">       
    <Row>
      <xsl:for-each select="*">
        <!-- resolve data-type and remember it as variable -->
        <xsl:variable name="concat('dataType', position())" select="dataType">
          <xsl:choose>
            <xsl:when test="TEXT">
              <xsl:value-of select=".">String</xsl:value-of>
            </xsl:when>
            <xsl:when test="DATE">
              <xsl:value-of select=".">DateTime</xsl:value-of>
            </xsl:when>
          </xsl:choose>
        </xsl:variable>
        <xsl:variable name="concat('dataFormat', position())" select="dataFormatter" >
          <!-- create style IDs for different formats -->
        </xsl:variable>
        <Cell>
          <Data ss:Type="String">
            <xsl:value-of select="title/." />
          </Data>
        </Cell>
      </xsl:for-each>
    </Row>              
  </xsl:template>

  <xsl:template match="elements/row/column">
    <xsl:for-each select="values">
      <Cell>
        <!-- resolve order within loop and pick correct data-type variable -->
        <xsl:variable name="type" select="concat('$dataType', position())" />
        <xsl:variable name="format" select="concat('$dataFormat', position())" />
        <Data ss:Type="$type">
          <xsl:value-of select="concat(normalize-space(.),' : ', $type)"/>
          <!-- check if data format is set -->
          <xsl:if test="//TODO">
            <xsl:value-of select="concat(' - ', $format)" />
          </xsl:if>
        </Data>
      </Cell>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

変数の名前として変数値を使用できないため、このバージョンは役に立ちません。定数値でなければなりません。データ全体の解析は何とか機能しますが、データ型とデータ形式を実装しようとすると壊れました。

編集: データ型とデータ形式に関する情報は、列とその見出しに関するすべての情報を保持する head 要素に配置されます。列は別のテンプレートで処理され、head 要素からの列定義に直接接続されません。関係は、要素の順序によってのみ維持されます。見出しだけでなく、各行と各セル (適切な列) のデータ型と可能なデータ形式 (オプション) の情報を処理する必要があります。

4

1 に答える 1

1

キーを使用して、見出し要素をその位置で検索できます

<xsl:key name="headings" match="heading" use="count(preceding-sibling::heading)" />

次に、列要素に配置されていると仮定すると、次のように、位置に基づいて関連付けられたデータ型を取得します

<xsl:variable 
   name="dataType" 
   select="key('headings', count(preceding-sibling::column))/dataType" /> 

つまり、行の最初の列要素については最初見出し要素を調べて、データ型を取得します。

XSLT について注意すべき点が他にもいくつかあります。まず、次のような動的変数名は使用できません

<xsl:variable name="concat('dataType', position())" select="dataType"> 

select属性を使用する場合、変数に空でないコンテンツを含めることもできません。

次に、出力属性で変数値を使用する場合は、属性値テンプレートを使用する必要があります。これを行う代わりに...

<Data ss:Type="$type">

あなたはこれをするでしょう

<Data ss:Type="{$type}">

また、 xsl:for-eachよりもxsl:apply-templatesを優先する必要があります。これは、コードの再利用、ネストされたコードの削減を促進し、XSLT の精神に沿っているためです。

とにかく、ここに完全な XSLT があります (作成された名前空間を使用していることに注意してください)。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:x="x" xmlns:ss="ss">
   <xsl:output method="xml" indent="yes"/>
   <xsl:key name="headings" match="heading" use="count(preceding-sibling::heading)"/>

   <xsl:template match="searchresult">
      <Worksheet>
         <Table x:FullColumns="1" x:FullRows="1">
            <xsl:apply-templates select="head"/>
            <xsl:apply-templates select="data/row"/>
         </Table></Worksheet>
   </xsl:template>

   <xsl:template match="head">
      <Row>
         <xsl:apply-templates select="heading"/>
      </Row>
   </xsl:template>

   <xsl:template match="heading">
      <Cell>
         <Data ss:Type="String">
            <xsl:value-of select="title"/>
         </Data>
      </Cell>
   </xsl:template>

   <xsl:template match="row">
      <row>
         <xsl:apply-templates select="column"/>
      </row>
   </xsl:template>

   <xsl:template match="column">
      <Cell>
         <xsl:variable name="dataType" select="key('headings', count(preceding-sibling::column))/dataType"/>
         <xsl:variable name="type">
            <xsl:choose>
               <xsl:when test="$dataType = 'TEXT'">
                  <xsl:text>String</xsl:text>
               </xsl:when>
               <xsl:when test="$dataType = 'DATE'">
                  <xsl:text>Date</xsl:text>
               </xsl:when>
            </xsl:choose>
         </xsl:variable>
         <xsl:variable name="dataFormat" select="key('headings', count(preceding-sibling::column))/dataFormat"/>
         <Data ss:Type="{$type}">
            <xsl:value-of select="concat(normalize-space(.),' : ', $type)"/>
            <xsl:if test="$dataFormat">
               <xsl:value-of select="concat(' - ', $dataFormat)"/>
            </xsl:if>
         </Data>
      </Cell>
   </xsl:template>
</xsl:stylesheet>

サンプル XML に適用すると、次のように出力されます。

<Worksheet xmlns:x="x" xmlns:ss="ss">
   <Table x:FullColumns="1" x:FullRows="1">
      <Row>
         <Cell>
            <Data ss:Type="String">Column1</Data>
         </Cell>
         <Cell>
            <Data ss:Type="String">Column2</Data>
         </Cell>
      </Row>
      <row>
         <Cell>
            <Data ss:Type="String">Hello : String</Data>
         </Cell>
         <Cell>
            <Data ss:Type="Date">2012-07-12 : Date - SHORT_DATE</Data>
         </Cell>
      </row>
      <row>
         <Cell>
            <Data ss:Type="String">Good bye : String</Data>
         </Cell>
         <Cell>
            <Data ss:Type="Date">2012-07-13 : Date - SHORT_DATE</Data>
         </Cell>
      </row>
   </Table>
</Worksheet>
于 2012-07-13T22:52:23.897 に答える