2

XMLドキュメントをCSVデータに変換するインテリジェントで効率的なXSLTを探しています。子ノードで可能なすべての要素を処理する必要があります。たとえば、XMLは次のようになります

<?xml version="1.0" encoding="ISO-8859-1"?>
<sObjects>
   <sObject>
     <Name>Raagu</Name>
     <BillingStreet>Hoskote</BillingStreet>
   </sObject>
   <sObject>
      <Name>Rajath</Name>
      <BillingStreet>BTM</BillingStreet>
      <age>25</age>
   </sObject>
   <sObject>
      <Name>Sarath</Name>
      <BillingStreet>Murgesh</BillingStreet>
      <location>Bangalore</location>
   </sObject>
</sObjects>

そして、私の出力CSVは次のようになります

Name,BillingStreet,age,location
Raagu,Hoskote,,
Rajath,BTM,25,
Sarath,Murgesh,,Bangalore

XMLに値がある場合でも、すべての行にCSV内のすべてのキーのフィールドが必要です。

以下は、ここでさまざまな例を見て思いついたXSLTコードです。

これは私が思いついたXSLTです

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:variable name="delimiter" select="','"/>

    <xsl:key name="field" match="sObject/*" use="name()"/>

    <xsl:template match="/">

        <xsl:for-each select="/*/*/*[generate-id()=generate-id(key('field', name())[1])]">
            <xsl:value-of select="name()"/>

            <xsl:if test="position() != last()">
                <xsl:value-of select="$delimiter"/>
            </xsl:if>
         </xsl:for-each>

        <xsl:text>&#xa;</xsl:text>

        <xsl:for-each select="/*/sObject">

            <xsl:variable name="property" select="." />
            <xsl:for-each select="$property/*">

                <xsl:variable name="value" select="." />
                <xsl:value-of select="$value"/>
                <xsl:if test="position() != last()">
                    <xsl:value-of select="$delimiter"/>
                </xsl:if>
                <xsl:if test="position() = last()">
                    <xsl:text>&#xa;</xsl:text>
                </xsl:if>

             </xsl:for-each>

        </xsl:for-each>


     </xsl:template>
 </xsl:stylesheet>

そしてそれはこれを出力して印刷します

Name,BillingStreet,age,location
Raagu,Hoskote
Rajath,BTM,25
Sarath,Murgesh,Bangalore

しかし、最初の行のすべてのキーについて、すべての行にそれらの値を何度も含める必要がありました。

XSLTコードを使用してこれを実現するのを手伝ってもらえますか?

4

1 に答える 1

7

2段階の解決策としてこれはどうですか

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:variable name="delimiter" select="','"/>

    <xsl:key name="field" match="/*/*/*" use="local-name()"/>

    <!-- variable containing the first occurrence of each field -->
    <xsl:variable name="allFields"
         select="/*/*/*[generate-id()=generate-id(key('field', local-name())[1])]" />

    <xsl:template match="/">
        <xsl:for-each select="$allFields">
            <xsl:value-of select="local-name()" />
            <xsl:if test="position() &lt; last()">
                <xsl:value-of select="$delimiter" />
            </xsl:if>
        </xsl:for-each>
        <xsl:text>&#10;</xsl:text>
        <xsl:apply-templates select="*/*" />
    </xsl:template>

    <xsl:template match="*">
        <xsl:variable name="this" select="." />
        <xsl:for-each select="$allFields">
            <xsl:value-of select="$this/*[local-name() = local-name(current())]" />
            <xsl:if test="position() &lt; last()">
                <xsl:value-of select="$delimiter" />
            </xsl:if>
        </xsl:for-each>
        <xsl:text>&#10;</xsl:text>
    </xsl:template>
</xsl:stylesheet>

ここでの秘訣は、allFields変数にはそれぞれの名前を持つ 1 つの要素が含まれるため、その行に実際に存在する要素だけでなく、各行に対して反復するのはこのノードのリストです。任意の名前空間などで XML をサポートしたいとおっしゃっているので/*/*/*、特定の要素名をハードコーディングするのではなく、次のようなパターンを使用しました (要素/*/*/*名に関係なく、ドキュメント要素の孫である要素に単純に一致します)。名前空間プレフィックスを無視するlocal-name()代わりに使用しています(をname()扱い<sObject>、まったく同じです)。<sObject xmlns="foo"><f:sObject xmlns:f="foo">

于 2013-03-05T14:47:37.890 に答える