簡體   English   中英

XSLT多個可重復的子節點

[英]XSLT multiple repeatable child nodes

我正在處理具有多個可重復的不同子節點類型的記錄集。 最終目標是創建CSV映射,由於嵌套,每個XML記錄可以映射到許多CSV訂單項。

我知道如何使用for-each來創建多條輸出線,但是我遇到了麻煩,因為有兩種不同的情況需要循環。

在以下示例中, App是基本記錄, SST_Interval是可重復的(1個或更多),而ReplacementPart可以具有0個或多個PartType

我想提取具有以下格式的CSV

app_id|base_vehicle_id|sst_interval_id|sst_interval_month|part_type_id

對於下面顯示的記錄,結果將如下所示

915152|18287|646|12|10007
915152|18287|646|12|12277
915152|18287|646|12|18159
915152|18287|32523|24|10007
915152|18287|32523|24|12277
915152|18287|32523646|24|18159

這是記錄

<App action="A" id="915152" ref="568874">
    <BaseVehicle id="18287" />
    <EngineVIN id="25" />
    <Note id="8722" vehicleattribute="no" />
    <Position id="1" />
    <MOTOR_Operation id="551841">
        <SkillCode>G</SkillCode>
        <Base_MOTOR_EWT minutes="1" />
        <SST_Interval id="646">
            <SST_IndicatorImage><![CDATA[sstgm140001]]></SST_IndicatorImage>
            <SST_IndicatorText><![CDATA[Change Engine Oil Soon]]></SST_IndicatorText>
            <SST_Frequency id="7" />
            <SST_IntervalMonth><![CDATA[12]]></SST_IntervalMonth>
            <SST_SevereService id="2080" />
            <SST_Note1 id="5117" />
        </SST_Interval>
        <SST_Interval id="32523">
            <SST_IndicatorImage><![CDATA[sstgm140001]]></SST_IndicatorImage>
            <SST_IndicatorText><![CDATA[Change Engine Oil Soon]]></SST_IndicatorText>
            <SST_Frequency id="7" />
            <SST_IntervalMonth><![CDATA[24]]></SST_IntervalMonth>
            <SST_SevereService id="2080" />
            <SST_Note1 id="5117" />
        </SST_Interval>
        <ReplacementPart>
            <PartType id="10007" servicetype_id="1" />
            <PartType id="12277" servicetype_id="1" />
            <PartType id="18159" servicetype_id="1" />
        </ReplacementPart>
    </MOTOR_Operation>
</App>

這是我嘗試過的XSLT。 如果您能指出正確的方向,我將不勝感激。

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" encoding="UTF-8" omit-xml-declaration="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="Header|Footer">
  </xsl:template>

  <xsl:template match="App">
    <xsl:apply-templates select="MOTOR_Operation/SST_Interval"/>
  </xsl:template>

  <xsl:template match="PartType">
    <xsl:apply-templates select="@id" mode="csv"/>
  </xsl:template>

  <xsl:template match="SST_Interval">
    <xsl:variable name="tmp">
      <xsl:apply-templates select="ancestor::App/@id" mode="csv"/>
      <xsl:apply-templates select="@id" mode="csv-nl"/>
    </xsl:variable>
    <xsl:for-each select="ancestor::App/MOTOR_Operation/ReplacementPart/PartType">
      <xsl:copy-of select="$tmp"/>
      <xsl:apply-templates select="." />
    </xsl:for-each>

  </xsl:template>

  <xsl:template match="text()|@*" mode="csv">
    <xsl:value-of select="concat(., '|')" />
  </xsl:template>

  <xsl:template match="text()|@*" mode="csv-nl">
    <xsl:value-of select="concat(., '&#xa;')" />
  </xsl:template>

</xsl:stylesheet>

我認為你離我們並不遙遠。 您只需要更改tmp變量的定義以包含更多字段,並對所有字段使用模式csv ,然后將匹配PartType的模板PartType為使用csv-nl

試試這個XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" encoding="UTF-8"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="Header|Footer">
  </xsl:template>

  <xsl:template match="App">
    <xsl:apply-templates select="MOTOR_Operation/SST_Interval"/>
  </xsl:template>

  <xsl:template match="PartType">
    <xsl:apply-templates select="@id" mode="csv-nl"/>
  </xsl:template>

  <xsl:template match="SST_Interval">
    <xsl:variable name="tmp">
      <xsl:apply-templates select="ancestor::App/@id" mode="csv"/>
      <xsl:apply-templates select="ancestor::App/BaseVehicle/@id" mode="csv"/>
      <xsl:apply-templates select="@id" mode="csv"/>
      <xsl:apply-templates select="SST_IntervalMonth" mode="csv"/>
    </xsl:variable>
    <xsl:for-each select="ancestor::App/MOTOR_Operation/ReplacementPart/PartType">
      <xsl:copy-of select="$tmp"/>
      <xsl:apply-templates select="." />
    </xsl:for-each>
  </xsl:template>

  <xsl:template match="text()|@*" mode="csv">
    <xsl:value-of select="concat(., '|')" />
  </xsl:template>

  <xsl:template match="text()|@*" mode="csv-nl">
    <xsl:value-of select="concat(., '&#xa;')" />
  </xsl:template>
</xsl:stylesheet>

編輯:作為對您的評論的回應,如果您想應對缺少的節點,我可能會不理會帶有模式的模板,而是將分隔符和換行符存儲在變量中。 那你可以做...

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" encoding="UTF-8" omit-xml-declaration="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:variable name="sep" select="'|'" />
  <xsl:variable name="nl" select="'&#xa;'" />

  <xsl:template match="App">
    <xsl:apply-templates select="MOTOR_Operation/SST_Interval"/>
  </xsl:template>

  <xsl:template match="PartType">
    <xsl:value-of select="@id" />
    <xsl:value-of select="$nl" />
  </xsl:template>

  <xsl:template match="SST_Interval">
    <xsl:variable name="tmp">
      <xsl:value-of select="ancestor::App/@id" />
      <xsl:value-of select="$sep" />
      <xsl:value-of select="ancestor::App/BaseVehicle/@id" />
      <xsl:value-of select="$sep" />
      <xsl:value-of select="@id"/>
      <xsl:value-of select="$sep" />
      <xsl:value-of select="SST_IntervalMonth"/>
      <xsl:value-of select="$sep" />
    </xsl:variable>
    <xsl:for-each select="ancestor::App/MOTOR_Operation/ReplacementPart/PartType">
      <xsl:copy-of select="$tmp"/>
      <xsl:apply-templates select="." />
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

編輯2:在沒有PartType記錄的情況下,請嘗試使用此代碼替換當前的xsl:for-each

    <xsl:variable name="PartTypes" select="ancestor::App/MOTOR_Operation/ReplacementPart/PartType" />
    <xsl:for-each select="$PartTypes">
      <xsl:copy-of select="$tmp"/>
      <xsl:apply-templates select="." />
    </xsl:for-each>
    <xsl:if test="not($PartTypes)">
      <xsl:copy-of select="$tmp"/>
      <xsl:value-of select="$nl" />
    </xsl:if>

您在問題中提出的XSLT完全可以正常工作。 主要問題很簡單,就是您在錯誤的地方使用了csv-nl模式。 此外,沒有任何內容可提供基本車輛ID和間隔月份字段,但添加這些字段看起來非常簡單。


更新:關於可選字段,盡管您可以使用XSL的條件構造,一種更自然的方法是簡單地允許相關字段的轉換不產生任何結果。 唯一的竅門是,對於您的應用程序,您必須始終輸出定界符,但這是由您插入定界符的特定方法引起的。

在該框架內工作的一種相當干凈的方法是無條件調用命名模板,該模板將轉換可選元素並添加定界符。 我已經更新了以下樣式表,以針對基本車輛ID進行演示。


但是,請允許我提出一些簡化的建議:

  • 當輸出方法是text時,您不需要omit-xml-declarations
  • 您不需要從輸入元素中strip-space ,因為您實際上並沒有使用任何文本節點; 只有屬性節點才有助於輸出。 另請注意,空白剝離僅適用於僅空白文本節點
  • 當輸入中沒有任何元素時,或者當樣式表和輸入的結構導致此類節點從不進行轉換時,無需取消<Header><Footer>元素。
  • 當您知道在選擇要轉換的點時只需要其id屬性時,在為PartType提供模板時就有點冗長
  • 利用模板參數可以使代碼更簡潔
  • 最好避免在不需要的地方避免使用xsl:for-each 通常,您可以簡單地將xsl:apply-templates應用於相同的節點,而不是
  • 作為個人風格的選擇,我寧願避免使用concat()函數,而可以直接提供xsl:value-of和/或xsl:text元素的序列

考慮到所有這些因素,我建議您在樣式表中使用以下變體:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" encoding="UTF-8"/>

  <xsl:template match="App">
    <xsl:apply-templates select="MOTOR_Operation/SST_Interval"/>
  </xsl:template>

  <xsl:template match="SST_Interval">
    <xsl:variable name="tmp">
      <xsl:apply-templates select="ancestor::App/@id" mode="csv"/>
      <xsl:call-template name="optional-vehicle"/>
      <xsl:apply-templates select="@id" mode="csv"/>
      <xsl:apply-templates select="SST_IntervalMonth" mode="csv"/>
    </xsl:variable>
    <xsl:apply-templates select="../ReplacementPart/PartType/@id" mode="csv-nl">
      <xsl:with-param name="prefix" select="$tmp"/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template name="optional-vehicle">
    <xsl:value-of select="ancestor::App/BaseVehicle/@id"/>
    <xsl:text>|</xsl:text>
  </xsl:template>

  <xsl:template match="node()|@*" mode="csv">
    <xsl:value-of select="." />
    <xsl:text>|</xsl:text>
  </xsl:template>

  <xsl:template match="node()|@*" mode="csv-nl">
    <xsl:param name="prefix"/>
    <xsl:value-of select="$prefix"/>
    <xsl:value-of select="." />
    <xsl:text>&#xa;</xsl:text>
  </xsl:template>

</xsl:stylesheet>

我建議一種不同的方法:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8" />

<xsl:template match="App">
    <xsl:variable name="common1">
        <xsl:value-of select="@id"/>
        <xsl:text>|</xsl:text>
        <xsl:value-of select="BaseVehicle/@id"/>
        <xsl:text>|</xsl:text>
    </xsl:variable>
    <xsl:for-each select="MOTOR_Operation/SST_Interval">
        <xsl:variable name="common2">
            <xsl:value-of select="$common1"/>
            <xsl:value-of select="@id"/>
            <xsl:text>|</xsl:text>
            <xsl:value-of select="SST_IntervalMonth"/>
            <xsl:text>|</xsl:text>
        </xsl:variable>
        <xsl:variable name="part-types" select="../ReplacementPart/PartType" />
        <xsl:for-each select="$part-types">
            <xsl:value-of select="$common2"/>
            <xsl:value-of select="@id"/>
            <xsl:text>&#10;</xsl:text>
        </xsl:for-each>
        <xsl:if test="not($part-types)">
            <xsl:value-of select="$common2"/>
            <xsl:text>&#10;</xsl:text>
        </xsl:if>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM