基于XSLT恒等变换实现新增分组表头记录的技术求助
Let's break this down with a concrete example—since abstract requirements can be tricky to map to code. First, I'll assume a sample input XML structure, then show you the XSLT that handles the header insertion when a target element's value changes across sibling records.
Sample Input XML
Suppose we have a list of order records, and we need to insert a header whenever the CustomerID value changes:
<Orders> <Order> <CustomerID>ALFKI</CustomerID> <OrderID>10643</OrderID> <Product>Chai</Product> </Order> <Order> <CustomerID>ALFKI</CustomerID> <OrderID>10692</OrderID> <Product>Chang</Product> </Order> <Order> <CustomerID>ANATR</CustomerID> <OrderID>10308</OrderID> <Product>Aniseed Syrup</Product> </Order> <Order> <CustomerID>ANATR</CustomerID> <OrderID>10625</OrderID> <Product>Chef Anton's Cajun Seasoning</Product> </Order> </Orders>
XSLT Solution (2.0+)
This uses the identity transform as the base, plus <xsl:for-each-group> to group records by the target element (CustomerID here). We'll insert a modified header at the start of each group:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- Identity transform: copy all nodes/attributes by default --> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <!-- Override the Orders element to handle grouping --> <xsl:template match="Orders"> <xsl:copy> <!-- Group Order records by CustomerID --> <xsl:for-each-group select="Order" group-by="CustomerID"> <!-- Insert modified header: use first group's Order as base, tweak content --> <Order> <CustomerID>HEADER: <xsl:value-of select="current-grouping-key()"/></CustomerID> <OrderID>---</OrderID> <Product>Customer Orders</Product> </Order> <!-- Output all records in the current group --> <xsl:apply-templates select="current-group()"/> </xsl:for-each-group> </xsl:copy> </xsl:template> </xsl:stylesheet>
What This Does
- Identity Transform Base: The first template ensures all existing nodes are copied exactly unless we override them—this preserves your original XML structure for everything except the grouped records.
- Grouping Logic: The
<xsl:for-each-group>clause groups allOrdersiblings byCustomerID. Whenever theCustomerIDchanges, a new group starts. - Header Insertion: For each group, we first create a modified
Ordernode (matching the sibling record format) as the header. Here, we prepend "HEADER:" to theCustomerID, setOrderIDto "---", and changeProductto a label—adjust these modifications to fit your specific needs. - Output Group Records: After inserting the header, we apply the identity transform to output all records in the current group.
If You're Stuck on XSLT 1.0
If you can't use XSLT 2.0+, you'll need Muenchian grouping instead. Here's a quick adaptation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:key name="orders-by-customer" match="Order" use="CustomerID"/> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <xsl:template match="Orders"> <xsl:copy> <!-- Process each unique CustomerID group --> <xsl:for-each select="Order[generate-id() = generate-id(key('orders-by-customer', CustomerID)[1])]"> <!-- Insert header --> <Order> <CustomerID>HEADER: <xsl:value-of select="CustomerID"/></CustomerID> <OrderID>---</OrderID> <Product>Customer Orders</Product> </Order> <!-- Output all orders in this customer's group --> <xsl:apply-templates select="key('orders-by-customer', CustomerID)"/> </xsl:for-each> </xsl:copy> </xsl:template> </xsl:stylesheet>
Key Notes for Your Specific Use Case
- Replace
CustomerIDwith the actual element you're checking for value changes. - Adjust the header modifications to match your requirements (e.g., change different elements, add new attributes, etc.).
- If your sibling records are nested under a different parent element, update the template match (instead of
Orders, match your parent element).
内容的提问来源于stack exchange,提问作者nfz11

