适配多表头Base64 CSV的XSLT多XML文档生成方案问询
问题描述
CSV文件的行经过Base64编码后存入XML对象模型,再映射到目标XML Schema。现有代码在单表头场景下运行正常,需要适配多表头场景:Base64解码后,第9位字段值为2的行是表头行,需要为每个表头生成独立的XML文档。
当前XSLT代码
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:extensions="http://www.infor.com/ION/XSL/extensions" exclude-result-prefixes="extensions" version="2.0"> <xsl:output method="xml" indent="yes"/> <xsl:template match="SyncMySupplierInvoice"> <SyncPurchaseOrder> <xsl:apply-templates select="ApplicationArea"/> <xsl:apply-templates select="DataArea"/> </SyncPurchaseOrder> </xsl:template> <xsl:template match="ApplicationArea"> <xsl:element name="ApplicationArea"> <xsl:copy-of select="*"/> </xsl:element> </xsl:template> <xsl:template match="DataArea"> <xsl:element name="DataArea"> <xsl:apply-templates select="Sync"/> <xsl:apply-templates select="MySupplierInvoice/RawData"/> </xsl:element> </xsl:template> <xsl:template match="Sync"> <xsl:element name="Load"> <TenantID>XXXXXXX</TenantID> <AccountingEntityID>510</AccountingEntityID> <LocationID/> <ActionCriteria> <ActionExpression actionCode="Add"/> </ActionCriteria> </xsl:element> </xsl:template> <xsl:template match="MySupplierInvoice/RawData"> <xsl:variable name="b64" select="extensions:decodeString(string(.))"/> <xsl:variable name="Header" select="$b64"/> <xsl:variable name="locCurrency" select="tokenize($Header,',')[position() = 8]"/> <SupplierInvoice> <SupplierInvoiceHeader> <DocumentID> <ID> <xsl:value-of select="tokenize($Header,',')[position()=1]"/> </ID> </DocumentID> <AlternateDocumentID> <ID> <xsl:value-of select="tokenize($Header,',')[position()=4]"/> </ID> </AlternateDocumentID> <DocumentDateTime> <xsl:value-of select=" tokenize($Header,',')[position()=6]"/> </DocumentDateTime> <LastModificationDateTime/> <Description> <xsl:value-of select="tokenize($Header,',')[position()=5]"/> </Description> <xsl:element name="TotalAmount"> <xsl:attribute name="currencyID"> <xsl:value-of select="$locCurrency"/> </attribute> <xsl:value-of select="tokenize($Header,',')[position() = 7]"/> </element> <RemitToParty/> <PurchaseOrderReference> <DocumentID> <ID/> </DocumentID> </PurchaseOrderReference> <BillFromParty> <PartyIDs> <ID> <xsl:value-of select="tokenize($Header,',')[position() = 3]"/> </ID> <TaxID/> <ID/> </PartyIDs> <Name/> </BillFromParty> </SupplierInvoiceHeader> <xsl:for-each select="tokenize($Header,' ')"> <xsl:variable name="Line" select="."/> <xsl:if test="position() != 1"> <!-- <xsl:if test="not(tokenize($Line,',')[position() = 9] = tokenize($Header,',')[position() = 9])"> --> <SupplierInvoiceLine> <LineNumber> <xsl:value-of select="tokenize($Line,',')[position() = 9]"/> </LineNumber> <Description> <xsl:value-of select="tokenize($Line,',')[position() =11]"/> <xsl:text>/</xsl:text> <xsl:value-of select="tokenize($Line,',')[position() =12]"/> <xsl:text>/</xsl:text> <xsl:value-of select="tokenize($Line,',')[position() =13]"/> <xsl:text>/</xsl:text> <xsl:value-of select="tokenize($Line,',')[position() =14]"/> <xsl:text>/</xsl:text> <xsl:value-of select="tokenize($Line,',')[position() =15]"/> <xsl:text>/</xsl:text> <xsl:value-of select="tokenize($Line,',')[position() =16]"/> </Description> <Quantity unitCode="PCE">1.00</Quantity> <ItemID> </ItemID> <UnitPrice> <xsl:element name="Amount"> <xsl:attribute name="currencyID"> <xsl:value-of select="$locCurrency"/> </attribute> <xsl:value-of select="tokenize($Line,',')[position() = 22]"/> </element> <PerQuantity unitCode="PCE">1</PerQuantity> </UnitPrice> <xsl:element name="ExtendedAmount"> <xsl:attribute name="currencyID"> <xsl:value-of select="$locCurrency"/> </attribute> <xsl:value-of select="tokenize($Line,',')[position() = 22]"/> </element> <PurchaseOrderReference> <DocumentID> <ID/> </DocumentID> </PurchaseOrderReference> <Tax> <xsl:element name="BasisAmount"> <xsl:attribute name="currencyID"> <xsl:value-of select="$locCurrency"/> </attribute> <xsl:value-of select="tokenize($Line,',')[position() = 22]"/> </element> <Calculation> <xsl:variable name="BaseAmount" select="tokenize($Line,',')[position() = 22]"/> <xsl:variable name="VatAmount" select="tokenize($Line,',')[position() = 23]"/> <!-- <xsl:variable name="VatAmount" select="substring-before($VatAmount, '
')"/> --> <xsl:variable name="VatAmount" as="xs:double" select="if($VatAmount castable as xs:double) then xs:double($VatAmount) else 0"/> <!-- <xsl:value-of select="$VatAmount"/> --> <RateNumeric> <xsl:value-of select="round($VatAmount div number($BaseAmount),3)"/> </RateNumeric> </Calculation> <xsl:element name="Amount"> <xsl:attribute name="currencyID"> <xsl:value-of select="$locCurrency"/> </attribute> <xsl:variable name="Amount" select="tokenize($Line,',')[position() = 23]"/> <xsl:value-of select="$Amount"/> </element> </Tax> </SupplierInvoiceLine> <!-- <xsl:if test="not(tokenize($Line,',')[position() = 9] = tokenize($Header,',')[position() = 9])"> --> <!-- Is this the right position to create a second XML Document Load.SupplierInvoice ? --> <!-- How do I have to configure the code ??? --> <!-- </xsl:if> --> </xsl:if> </xsl:for-each> </SupplierInvoice> </xsl:template> </xsl:stylesheet>
CSV示例
11,Kostenrechnung,200000001,5511,701 -XXXXX,2022-08-15T09:00:00Z,1191.95,EUR,2,XXXXX,,,,,,,,,,,Haben,1191.95,0 11,Kostenrechnung,200000001,5511,701 - XXXXX,2022-08-15T09:00:00Z,1191.95,EUR,3,XXXXX,49450,210001,,,,,Einkauf,DE,120,Netto,Soll,328.23,22.98 11,Kostenrechnung,200000001,5511,701 - XXXXX,2022-08-15T09:00:00Z,1191.95,EUR,4,XXXXX,49450,210001,,,,,Einkauf,,0,Netto,Soll,254.54,0 11,Kostenrechnung,200000001,5511,701 - XXXXX,2022-08-15T09:00:00Z,1191.95,EUR,5,XXXXX,49480,410002,,,,,Einkauf,,0,Netto,Soll,78,0 11,Kostenrechnung,200000001,5511,701 - XXXXX,2022-08-15T09:00:00Z,1191.95,EUR,6,XXXXX,49800,999998,3000,,,,Einkauf,,0,Netto,Soll,139.8,0 11,Kostenrechnung,200000001,5511,701 - XXXXX,2022-08-15T09:00:00Z,1191.95,EUR,7,XXXXX,49690,210001,,,,,Einkauf,,0,Netto,Soll,368.4,0 13,Kostenrechnung,200000003,5512,701 - XXXXX,2022-08-30T09:00:00Z,773.55,EUR,2,XXXXX,,,,,,,,,,,Haben,773.55,0 13,Kostenrechnung,200000003,5512,701 - XXXXX,2022-08-30T09:00:00Z,773.55,EUR,3,XXXXX,49450,210001,,,,,Einkauf,DEU,000,Netto,Soll,36.40,0.00 13,Kostenrechnung,200000003,5512,701 - XXXXX,2022-08-30T09:00:00Z,773.55,EUR,4,XXXXX,49450,210001,,,,,Einkauf,DEU,000,Netto,Soll,54.00,0.00 13,Kostenrechnung,200000003,5512,701 - XXXXX,2022-08-30T09:00:00Z,773.55,EUR,5,XXXXX,49450,210001,,,,,Einkauf,DEU,000,Netto,Soll,97.20,0.00 13,Kostenrechnung,200000003,5512,701 - XXXXX,2022-08-30T09:00:00Z,773.55,EUR,6,XXXXX,49800,999998,3000,,,,Einkauf,DEU,000,Netto,Soll,488.40,0.00 13,Kostenrechnung,200000003,5512,701 - XXXXX,2022-08-30T09:00:00Z,773.55,EUR,7,XXXXX,49800,999998,3000,,,,Einkauf,DEU,132,Netto,Soll,81.97,15.58
修改后的XSLT方案
核心思路:
- 将解码后的CSV按行拆分,过滤空行
- 使用
xsl:for-each-group按表头行(第9字段=2)分组,每组包含一个表头和后续的明细行 - 为每个分组生成独立的
SyncPurchaseOrder文档
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:extensions="http://www.infor.com/ION/XSL/extensions" exclude-result-prefixes="extensions" version="2.0"> <xsl:output method="xml" indent="yes"/> <!-- 主模板:为每个发票分组生成独立的SyncPurchaseOrder --> <xsl:template match="SyncMySupplierInvoice"> <xsl:variable name="appArea" select="ApplicationArea"/> <xsl:variable name="syncNode" select="DataArea/Sync"/> <xsl:variable name="decodedCsv" select="extensions:decodeString(string(DataArea/MySupplierInvoice/RawData))"/> <!-- 拆分CSV为行,过滤空行 --> <xsl:variable name="csvLines" select="tokenize($decodedCsv, ' ')[normalize-space(.) ne '']"/> <!-- 按表头行分组 --> <xsl:for-each-group select="$csvLines" group-starting-with=".[tokenize(., ',')[9] = '2']"> <SyncPurchaseOrder> <xsl:copy-of select="$appArea"/> <DataArea> <xsl:copy-of select="$syncNode"/> <!-- 处理当前分组的发票 --> <xsl:call-template name="processInvoiceGroup"> <xsl:with-param name="headerLine" select="current-group()[1]"/> <xsl:with-param name="detailLines" select="current-group()[position() > 1]"/> </xsl:call-template> </DataArea> </SyncPurchaseOrder> </xsl:for-each-group> </xsl:template> <!-- 处理单个发票分组:表头+明细 --> <xsl:template name="processInvoiceGroup"> <xsl:param name="headerLine"/> <xsl:param name="detailLines"/> <xsl:variable name="headerFields" select="tokenize($headerLine, ',')"/> <xsl:variable name="locCurrency" select="$headerFields[8]"/> <SupplierInvoice> <SupplierInvoiceHeader> <DocumentID> <ID><xsl:value-of select="$headerFields[1]"/></ID> </DocumentID> <AlternateDocumentID> <ID><xsl:value-of select="$headerFields[4]"/></ID> </AlternateDocumentID> <DocumentDateTime><xsl:value-of select="$headerFields[6]"/></DocumentDateTime> <LastModificationDateTime/> <Description><xsl:value-of select="$headerFields[5]"/></Description> <TotalAmount currencyID="{$locCurrency}"> <xsl:value-of select="$headerFields[7]"/> </TotalAmount> <RemitToParty/> <PurchaseOrderReference> <DocumentID><ID/></DocumentID> </PurchaseOrderReference> <BillFromParty> <PartyIDs> <ID><xsl:value-of select="$headerFields[3]"/></ID> <TaxID/> <ID/> </PartyIDs> <Name/> </BillFromParty> </SupplierInvoiceHeader> <!-- 处理明细行 --> <xsl:for-each select="$detailLines"> <xsl:variable name="lineFields" select="tokenize(., ',')"/> <SupplierInvoiceLine> <LineNumber><xsl:value-of select="$lineFields[9]"/></LineNumber> <Description> <xsl:value-of select="$lineFields[11]"/> <xsl:text>/</xsl:text> <xsl:value-of select="$lineFields[12]"/> <xsl:text>/</xsl:text> <xsl:value-of select="$lineFields[13]"/> <xsl:text>/</xsl:text> <xsl:value-of select="$lineFields[14]"/> <xsl:text>/</xsl:text> <xsl:value-of select="$lineFields[15]"/> <xsl:text>/</xsl:text> <xsl:value-of select="$lineFields[16]"/> </Description> <Quantity unitCode="PCE">1.00</Quantity> <ItemID/> <UnitPrice> <Amount currencyID="{$locCurrency}"><xsl:value-of select="$lineFields[2
相关产品推荐
相关产品推荐

