[英]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(., '
')" />
</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(., '
')" />
</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="'
'" />
<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進行演示。
但是,請允許我提出一些簡化的建議:
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>
</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> </xsl:text>
</xsl:for-each>
<xsl:if test="not($part-types)">
<xsl:value-of select="$common2"/>
<xsl:text> </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.