2010-08-26 35 views
5

Tôi mới chuyển đổi xsl và tôi có một câu hỏi. Tôi lặp thông qua một xml như thế này:xsl transformation

<PO> 
<Items> 
    <Item> 
    <Price>2</Price> 
    <Quantity>5</Quantity> 
    </Item> 
    <Item> 
    <Price>3</Price> 
    <Quantity>2</Quantity> 
    </Item>  
</Items> 
<QuantityTotal></QuantityTotal> 
</PO> 

Bây giờ tôi muốn chèn một giá trị trong nút QuantityTotal:
Giá trị là tổng của giá * số lượng của tất cả các mặt hàng, trong trường hợp này (2 * 5) + (3 * 2) = 16 Làm thế nào tôi có thể làm điều này, tôi đã thử nó với một vòng lặp và các biến, nhưng biến là không thay đổi vì vậy tôi không biết làm thế nào tôi có thể đạt được điều này.

Thx để được trợ giúp của bạn

+1

+1 câu hỏi hay –

+0

Câu hỏi hay (+1). Xem câu trả lời của tôi cho các giải pháp trong cả XSLT 1.0 (không yêu cầu mở rộng) và XSLT 2.0 –

+0

Xem thêm http://stackoverflow.com/questions/436998/multiply-2-numbers-and-then-sum-with-xslt và http : // stackoverflow.com/questions/1333558/xslt-to-sum-product-of-two-attributes – harpo

Trả lời

4

Dưới đây là một giải pháp XSLT - không có chức năng mở rộng cần:

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

<xsl:template match="node()|@*"> 
    <xsl:copy> 
     <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="QuantityTotal"> 
    <xsl:copy> 
    <xsl:call-template name="sumProducts"> 
    <xsl:with-param name="pNodes" select="../Items/Item"/> 
    </xsl:call-template> 
    </xsl:copy> 
</xsl:template> 

<xsl:template name="sumProducts"> 
    <xsl:param name="pNodes"/> 
    <xsl:param name="pSum" select="0"/> 
    <xsl:param name="pEname1" select="'Price'"/> 
    <xsl:param name="pEname2" select="'Quantity'"/> 

    <xsl:choose> 
    <xsl:when test="not($pNodes)"> 
    <xsl:value-of select="$pSum"/> 
    </xsl:when> 
    <xsl:otherwise> 
    <xsl:call-template name="sumProducts"> 
     <xsl:with-param name="pNodes" select= 
     "$pNodes[position() > 1]"/> 
     <xsl:with-param name="pSum" select= 
     "$pSum 
     + 
     $pNodes[1]/*[name()=$pEname1] 
     * 
     $pNodes[1]/*[name()=$pEname2] 
     "/> 
    </xsl:call-template> 
    </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 
</xsl:stylesheet> 

khi chuyển đổi này được áp dụng trên tài liệu XML được cung cấp:

<PO> 
    <Items> 
     <Item> 
      <Price>2</Price> 
      <Quantity>5</Quantity> 
     </Item> 
     <Item> 
      <Price>3</Price> 
      <Quantity>2</Quantity> 
     </Item> 
    </Items> 
    <QuantityTotal></QuantityTotal> 
</PO> 

kết quả mong muốn được sản xuất:

<PO> 
    <Items> 
     <Item> 
     <Price>2</Price> 
     <Quantity>5</Quantity> 
     </Item> 
     <Item> 
     <Price>3</Price> 
     <Quantity>2</Quantity> 
     </Item> 
    </Items> 
    <QuantityTotal>16</QuantityTotal> 
</PO> 
+0

+1 cho giải pháp XSLT 1.0 tốt và XSLT 2.0! –

1

Đây là giải pháp sử dụng XSLT2, trong đó tập hợp nút là đối tượng hạng nhất. Trong XSLT1, bạn cần sử dụng phần mở rộng tập hợp nút.

Giải thích dưới đây:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0"> 

    <xsl:template match="@*|node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:variable name="extendedItems" as="xs:integer*"> 
     <xsl:for-each select="//Item"> 
      <xsl:value-of select="./Price * ./Quantity"/> 
     </xsl:for-each> 
    </xsl:variable> 

    <xsl:variable name="total"> 
     <xsl:value-of select="sum($extendedItems)"/> 
    </xsl:variable> 

    <xsl:template match="//QuantityTotal"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*"/> 
      <xsl:value-of select="$total"/> 
     </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

Cách tiếp cận ở đây là sử dụng một "Identity chuyển hóa" để sao chép các tài liệu, trong khi thực hiện các tính toán và chèn kết quả vào đầu ra QuantityTotal mẫu. Mẫu đầu tiên sao chép đầu vào vào đầu ra nhưng bị ghi đè bởi một mẫu cụ thể hơn cho QuantityTotal ở dưới cùng. Khai báo biến đầu tiên tạo ra một danh sách các chi phí mở rộng, và định nghĩa biến thứ hai tính tổng chi phí để tạo ra tổng số. Tổng số sau đó được chèn vào nút QuantityTotal.

Chìa khóa để hiểu XSL là nó mang tính khai báo trong tự nhiên. Lỗi khái niệm phổ biến nhất được thực hiện bởi hầu hết tất cả người mới bắt đầu là giả định rằng biểu định kiểu là một chương trình tuần tự xử lý tài liệu XML đầu vào. Trong thực tế, đó là cách khác xung quanh. Công cụ XSL đọc tài liệu XML. và đối với mỗi thẻ mới, nó gặp trong biểu định kiểu cho đối sánh "tốt nhất", thực thi mẫu đó.

EDIT:

Dưới đây là một phiên bản xslt1.1 làm việc với Saxon 6,5

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:ex="http://exslt.org/common" 
    extension-element-prefixes="ex" 
    version="1.1"> 
    <xsl:template match="@*|node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:variable name="extendedItems"> 
     <xsl:for-each select="//Item"> 
      <extended> 
      <xsl:value-of select="./Price * ./Quantity"/> 
      </extended> 
      </xsl:for-each> 
    </xsl:variable> 
    <xsl:variable name="total"> 
     <xsl:value-of select="sum(ex:node-set($extendedItems/extended))"/> 
    </xsl:variable> 
    <xsl:template match="//QuantityTotal"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*"/> 
      <xsl:value-of select="$total"/> 
     </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 
+0

cảm ơn bạn đã trả lời, nhưng nó vẫn không hoạt động như nó được cho là vậy. Kết quả tôi nhận được bây giờ là 106, cái gì là nó tính toán mục đầu tiên (kết quả = 10) và mục thứ hai (kết quả = 6) và do đó biến extendItems giá trị của nó trở thành "106". Ngoài ra khi tôi sử dụng hàm sum() cho tổng số, nó ném một lỗi mà tôi phải sử dụng một bộ nút() để mã của tôi cho tổng trở thành như thế này: sum (msxsl: node-set ($ extendItems)). Tôi đang làm gì sai? Thx trước –

+0

Bạn đang chạy điều này với XSLT1. Bạn có máy biến áp XSLT2 có sẵn không? –

+0

Bạn đang sử dụng bộ xử lý XSL nào? –

2

Bên cạnh câu trả lời tuyệt vời Dimitre của, kiểu này có cách tiếp cận khác:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:template match="node()|@*"> 
     <xsl:copy> 
      <xsl:apply-templates select="node()|@*"/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="QuantityTotal"> 
     <xsl:copy> 
      <xsl:apply-templates select="../Items/Item[1]" mode="sum"/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="Item" mode="sum"> 
     <xsl:param name="pSum" select="0"/> 
     <xsl:variable name="vNext" select="following-sibling::Item[1]"/> 
     <xsl:variable name="vSum" select="$pSum + Price * Quantity"/> 
     <xsl:apply-templates select="$vNext" mode="sum"> 
      <xsl:with-param name="pSum" select="$vSum"/> 
     </xsl:apply-templates> 
     <xsl:if test="not($vNext)"> 
      <xsl:value-of select="$vSum"/> 
     </xsl:if> 
    </xsl:template> 
</xsl:stylesheet> 

Output:

<PO> 
    <Items> 
     <Item> 
      <Price>2</Price> 
      <Quantity>5</Quantity> 
     </Item> 
     <Item> 
      <Price>3</Price> 
      <Quantity>2</Quantity> 
     </Item> 
    </Items> 
    <QuantityTotal>16</QuantityTotal> 
</PO> 
+0

@Alejandro: Câu trả lời hay (+1). –

Các vấn đề liên quan