如何将XML中P01/P02/P03按PositionNo分组嵌套入<Position>元素并排序
XML结构转换方案
转换要求
- 新增
<Position></Position>作为父节点 - 相同
PositionNo对应的PO1、P02、P03节点需嵌套到同一个<Position>节点内 - 所有
<Position>节点最终按PositionNo的数值升序排序
源XML示例(片段)
<PO1> <ClientNo>ABCD</ClientNo> <OrderNo>10000014</OrderNo> <FunctionNo>10</FunctionNo> <PositionNo>1</PositionNo> <ArticleNo>148756833112</ArticleNo> <QuantityLoading></QuantityLoading> <LMSM></LMSM> <QuantityUnit1>300000</QuantityUnit1> <UnitLevel>1</UnitLevel> <Obsolete1></Obsolete1> <Obsolete2></Obsolete2> <NetWeight>105600</NetWeight> <GrossWeight>105600</GrossWeight> <WeightUnit></WeightUnit> <ArticleDamageStatusFlag>0</ArticleDamageStatusFlag> <CustomStatusFlag>F</CustomStatusFlag> <PickingAdvice></PickingAdvice> <StockManagementFlag></StockManagementFlag> <LoadingNo></LoadingNo> <MeansOfTransportNo></MeansOfTransportNo> <StockNo>STANDARD</StockNo> <PackageId></PackageId> <PosIndex>101</PosIndex> <LMSMID></LMSMID> <Unit1Factor>1000</Unit1Factor> <Volume>0</Volume> <VolumeUnit>CBM</VolumeUnit> <LMSMQtyFactor></LMSMQtyFactor> <MeasurementUnitLevel></MeasurementUnitLevel> <MeasurementName></MeasurementName> <MeasurementUnit>1</MeasurementUnit> <PercentalExcessDelivery></PercentalExcessDelivery> <SmallestIndivisibleUnit></SmallestIndivisibleUnit> <AccuracyOfQty></AccuracyOfQty> <MTQQty></MTQQty> <PackingInstruction></PackingInstruction> <PickingListReference></PickingListReference> </PO1> <P02> <ClientNo>ABCD</ClientNo> <OrderNo>10000014</OrderNo> <FunctionNo>10</FunctionNo> <PositionNo>1</PositionNo> <LotNo></LotNo> <ArticleNoRecipient></ArticleNoRecipient> <ExternalReference></ExternalReference> <PositionText1></PositionText1> <ExpiryDate></ExpiryDate> <ProductionDate></ProductionDate> <ProductionTime></ProductionTime> <FreeSlotSelectionFlag></FreeSlotSelectionFlag> <CustomerOrderNo></CustomerOrderNo> <OrderPositionNo></OrderPositionNo> <CountryOfOrigin></CountryOfOrigin> <ExternalReference2></ExternalReference2> <PosIndex>101</PosIndex> <Price1>0</Price1> <Price2>0</Price2> <Document></Document> <ArticleVariantNumber></ArticleVariantNumber> <SetType></SetType> <DestinyStock></DestinyStock> <CurrencyPrice1></CurrencyPrice1> <CurrencyPrice2></CurrencyPrice2> <PrintLanguage></PrintLanguage> <ArticleGroup></ArticleGroup> <LatestShippingDate></LatestShippingDate> <ExpirationDate></ExpirationDate> <ShelfLife></ShelfLife> <SupplierArticleNumber></SupplierArticleNumber> </P02> <P03> <ClientNo>ABCD</ClientNo> <OrderNo>10000014</OrderNo> <FunctionNo>10</FunctionNo> <PositionNo>1</PositionNo> <PositionText2></PositionText2> <PositionText3></PositionText3> <PosIndex>101</PosIndex> <EAN></EAN> </P03> <PO1> <ClientNo>ABCD</ClientNo> <OrderNo>10000014</OrderNo> <FunctionNo>10</FunctionNo> <PositionNo>2</PositionNo> <ArticleNo>180030550211</ArticleNo> <QuantityLoading></QuantityLoading> <LMSM></LMSM> <QuantityUnit1>200000</QuantityUnit1> <UnitLevel>1</UnitLevel> <Obsolete1></Obsolete1> <Obsolete2></Obsolete2> <NetWeight>640000</NetWeight> <GrossWeight>640000</GrossWeight> <WeightUnit></WeightUnit> <ArticleDamageStatusFlag>0</ArticleDamageStatusFlag> <CustomStatusFlag>F</CustomStatusFlag> <PickingAdvice></PickingAdvice> <StockManagementFlag></StockManagementFlag> <LoadingNo></LoadingNo> <MeansOfTransportNo></MeansOfTransportNo> <StockNo>STANDARD</StockNo> <PackageId></PackageId> <PosIndex>201</PosIndex> <LMSMID></LMSMID> <Unit1Factor>1000</Unit1Factor> <Volume>35945</Volume> <VolumeUnit>CBM</VolumeUnit> <LMSMQtyFactor></LMSMQtyFactor> <MeasurementUnitLevel></MeasurementUnitLevel> <MeasurementName></MeasurementName> <MeasurementUnit>1</MeasurementUnit> <PercentalExcessDelivery></PercentalExcessDelivery> <SmallestIndivisibleUnit></SmallestIndivisibleUnit> <AccuracyOfQty></AccuracyOfQty> <MTQQty></MTQQty> <PackingInstruction></PackingInstruction> <PickingListReference></PickingListReference> </PO1> <P02> <ClientNo>ABCD</ClientNo> <OrderNo>10000014</OrderNo> <FunctionNo>10</FunctionNo> <PositionNo>2</PositionNo> <LotNo></LotNo> <ArticleNoRecipient></ArticleNoRecipient> <ExternalReference></ExternalReference> <PositionText1></PositionText1> <ExpiryDate></ExpiryDate> <ProductionDate></ProductionDate> <ProductionTime></ProductionTime> <FreeSlotSelectionFlag></FreeSlotSelectionFlag> <CustomerOrderNo></CustomerOrderNo> <OrderPositionNo></OrderPositionNo> <CountryOfOrigin></CountryOfOrigin> <ExternalReference2></ExternalReference2> <PosIndex>201</PosIndex> <Price1>0</Price1> <Price2>0</Price2> <Document></Document> <ArticleVariantNumber></ArticleVariantNumber> <SetType></SetType> <DestinyStock></DestinyStock> <CurrencyPrice1></CurrencyPrice1> <CurrencyPrice2></CurrencyPrice2> <PrintLanguage></PrintLanguage> <ArticleGroup></ArticleGroup> <LatestShippingDate></LatestShippingDate> <ExpirationDate></ExpirationDate> <ShelfLife></ShelfLife> <SupplierArticleNumber></SupplierArticleNumber> </P02> <P03> <ClientNo>ABCD</ClientNo> <OrderNo>10000014</OrderNo> <FunctionNo>10</FunctionNo> <PositionNo>2</PositionNo> <PositionText2></PositionText2> <PositionText3></PositionText3> <PosIndex>201</PosIndex> <EAN></EAN> </P03>
预期输出示例
<Position> <PO1> <ClientNo>ABCD</ClientNo> <OrderNo>10000014</OrderNo> <FunctionNo>10</FunctionNo> <PositionNo>1</PositionNo> <!-- 其余节点省略 --> </PO1> <P02> <ClientNo>ABCD</ClientNo> <OrderNo>10000014</OrderNo> <FunctionNo>10</FunctionNo> <PositionNo>1</PositionNo> <!-- 其余节点省略 --> </P02> <P03> <ClientNo>ABCD</ClientNo> <OrderNo>10000014</OrderNo> <FunctionNo>10</FunctionNo> <PositionNo>1</PositionNo> <!-- 其余节点省略 --> </P03> </Position> <Position> <PO1> <ClientNo>ABCD</ClientNo> <OrderNo>10000014</OrderNo> <FunctionNo>10</FunctionNo> <PositionNo>2</PositionNo> <!-- 其余节点省略 --> </PO1> <P02> <ClientNo>ABCD</ClientNo> <OrderNo>10000014</OrderNo> <FunctionNo>10</FunctionNo> <PositionNo>2</PositionNo> <!-- 其余节点省略 --> </P02> <P03> <ClientNo>ABCD</ClientNo> <OrderNo>10000014</OrderNo> <FunctionNo>10</FunctionNo> <PositionNo>2</PositionNo> <!-- 其余节点省略 --> </P03> </Position>
实现代码(XSLT 2.0)
使用XSLT的分组功能可直接实现该需求,代码如下:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <!-- 匹配根节点,可根据实际的根节点名称调整match规则 --> <xsl:template match="/*"> <xsl:copy> <!-- 按PositionNo对所有PO1、P02、P03节点分组 --> <xsl:for-each-group select="*[self::PO1 or self::P02 or self::P03]" group-by="PositionNo"> <!-- 按PositionNo数值升序排序 --> <xsl:sort select="xs:integer(current-grouping-key())" order="ascending"/> <Position> <!-- 按节点名称排序,保证PO1在前、P02次之、P03最后 --> <xsl:apply-templates select="current-group()"> <xsl:sort select="name()" order="ascending"/> </xsl:apply-templates> </Position> </xsl:for-each-group> </xsl:copy> </xsl:template> <!-- 节点原样复制模板 --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
注意事项
- 需使用支持XSLT 2.0的处理器运行上述代码,推荐使用Saxon
- 若源XML存在其他非PO1/P02/P03的节点需要保留,可在根节点匹配模板中添加对应复制逻辑
内容的提问来源于stack exchange,提问作者Daniel Schotter
相关产品推荐
相关产品推荐

