2011-01-30 20 views
5

Hãy nói rằng tôi có nút XML này:xsl: chuyển đổi một danh sách vào một bảng 2-D

<items> 
    <item>...<item> 
    <item>...<item> 
    <item>...<item> 
    <item>...<item> 
    <item>...<item> 
    ... 
</items> 

nơi có N item nút.

Bây giờ tôi muốn chuyển đổi nó thành một bảng HTML với 4 cột. (ví dụ: nếu N = 12, có 3 hàng hoàn chỉnh và nếu N = 27, có 7 hàng, ô cuối cùng có 3 ô)

Làm cách nào để tôi thực hiện việc này?

gọi ruột của tôi là để làm theo cách này, nơi {{something}} là những gì tôi không biết làm thế nào để thực hiện:

<xsl:template match="items"> 
    <table> 
     <xsl:call-template name="partition-items"> 
     <xsl:with-param name="skip" select="0" /> 
     </xsl:call-template> 
    </table> 
</xsl:template> 

<xsl:template name="partition-items"> 
    <xsl:param name="skip" /> 
    {{ if # of items in current node > $skip, 
      output a row, 
      and call partition-items($skip+4) 
    }} 
<xsl:template /> 

Những mảnh Tôi không biết làm thế nào để thực hiện, là

  • làm thế nào để làm cho một vị để kiểm tra # của item yếu tố trong nút hiện
  • thế nào để có được những yếu tố thứ N item trong nút hiện

Update từ bình luận

Làm thế nào để pad hàng cuối cùng với trống <td /> yếu tố này để mỗi hàng chứa chính xác các tế bào muốn?

+1

Câu hỏi hay, +1. Xem câu trả lời của tôi cho giải pháp có lẽ ngắn nhất mà thậm chí không sử dụng bất kỳ đệ quy rõ ràng nào. :) –

+0

Đã thêm giải pháp XSLT 2.0. :) –

Trả lời

3

Đó là giải pháp làm việc của tôi.

Vì bạn đã không cung cấp kết quả mong muốn, điều này có thể không phù hợp với nhu cầu của bạn.

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="html" indent="yes"/> 

    <xsl:template match="/*"> 
     <table> 
      <xsl:call-template name="make-columns"> 
       <xsl:with-param name="nodelist" select="item"/> 
      </xsl:call-template> 
     </table> 
    </xsl:template> 

    <xsl:template name="make-columns"> 
     <xsl:param name="nodelist"/> 
     <xsl:param name="columns-number" select="4"/> 

     <tr> 
      <xsl:apply-templates select="$nodelist[ 
          not(position() > $columns-number) 
          ]"/> 
     </tr> 

     <!-- If some nodes are left, recursively call current 
     template, passing only nodes that are left --> 
     <xsl:if test="count($nodelist) > $columns-number"> 
      <xsl:call-template name="make-columns"> 
       <xsl:with-param name="nodelist" select="$nodelist[ 
             position() > $columns-number 
             ]"/> 
      </xsl:call-template> 
     </xsl:if> 
    </xsl:template> 

    <xsl:template match="item"> 
     <td> 
      <xsl:apply-templates/> 
     </td> 
    </xsl:template> 

</xsl:stylesheet> 

Kiểm tra đầu vào:

<items> 
    <item>1</item> 
    <item>2</item> 
    <item>3</item> 
    <item>4</item> 
    <item>5</item> 
    <item>6</item> 
    <item>7</item> 
    <item>8</item> 
    <item>9</item> 
    <item>10</item> 
    <item>11</item> 
    <item>12</item> 
    <item>13</item> 
    <item>14</item> 
    <item>15</item> 
    <item>16</item> 
    <item>17</item> 
    <item>18</item> 
    <item>19</item> 
    <item>20</item> 
    <item>21</item> 
    <item>22</item> 
    <item>23</item> 
    <item>24</item> 
    <item>25</item> 
    <item>26</item> 
    <item>27</item> 
</items> 

Output:

<table> 
    <tr> 
     <td>1</td> 
     <td>2</td> 
     <td>3</td> 
     <td>4</td> 
    </tr> 
    <tr> 
     <td>5</td> 
     <td>6</td> 
     <td>7</td> 
     <td>8</td> 
    </tr> 
    <tr> 
     <td>9</td> 
     <td>10</td> 
     <td>11</td> 
     <td>12</td> 
    </tr> 
    <tr> 
     <td>13</td> 
     <td>14</td> 
     <td>15</td> 
     <td>16</td> 
    </tr> 
    <tr> 
     <td>17</td> 
     <td>18</td> 
     <td>19</td> 
     <td>20</td> 
    </tr> 
    <tr> 
     <td>21</td> 
     <td>22</td> 
     <td>23</td> 
     <td>24</td> 
    </tr> 
    <tr> 
     <td>25</td> 
     <td>26</td> 
     <td>27</td> 
    </tr> 
</table> 

Do lưu ý: bạn có thể vượt qua cột số tự động.

Yêu cầu bổ sung và chỉnh sửa.

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:my="http://localhost" 
    exclude-result-prefixes="my"> 
    <xsl:output method="html" indent="yes"/> 

    <my:layout> 
     <td/><td/><td/><td/> 
     <td/><td/><td/><td/> 
     <td/><td/><td/><td/> 
     <td/><td/><td/><td/> 
    </my:layout> 

    <xsl:template match="/*"> 
     <table> 
      <xsl:call-template name="make-columns"> 
       <xsl:with-param name="nodelist" select="item"/> 
      </xsl:call-template> 
     </table> 
    </xsl:template> 

    <xsl:template name="make-columns"> 
     <xsl:param name="nodelist"/> 
     <xsl:param name="columns-number" select="4"/> 

     <tr> 
      <xsl:apply-templates select="$nodelist[ 
          not(position() > $columns-number) 
          ]"/> 
      <xsl:if test="count($nodelist) &lt; $columns-number"> 
       <xsl:copy-of select="document('')/*/my:layout/td[ 
        position() &lt;= $columns-number - count($nodelist) 
        ]"/> 
      </xsl:if> 
     </tr> 

     <!-- If some nodes are left, recursively call current 
     template, passing only nodes that are left --> 
     <xsl:if test="count($nodelist) > $columns-number"> 
      <xsl:call-template name="make-columns"> 
       <xsl:with-param name="nodelist" select="$nodelist[ 
             position() > $columns-number 
             ]"/> 
      </xsl:call-template> 
     </xsl:if> 
    </xsl:template> 

    <xsl:template match="item"> 
     <td> 
      <xsl:apply-templates/> 
     </td> 
    </xsl:template> 

</xsl:stylesheet> 

Nó có thể được áp dụng cho các mẫu trước đó hoặc XML ngắn gọn này:

<items> 
    <item>1</item> 
</items> 

Kết quả sẽ là:

<table> 
    <tr> 
     <td>1</td> 
     <td xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="http://localhost"></td> 
     <td xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="http://localhost"></td> 
     <td xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="http://localhost"></td> 
    </tr> 
</table> 

Do lưu ý:

  1. Dữ liệu được mã hóa cứng để thêm các phần tử, khi có ít hơn item phần tử hơn số cột.
  2. Yếu tố mã hóa bổ sung, nếu số lượng cột sẽ thay đổi.

Nếu không bao giờ có ít thành phần hơn số cột, bạn chỉ có thể áp dụng cho các thành phần item có cùng vị từ và khác nhau mode.

Và chỉnh sửa cuối cùng. Với vòng lặp được tính.

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="html" indent="yes"/> 

    <xsl:template match="/*"> 
     <table> 
      <xsl:call-template name="make-columns"> 
       <xsl:with-param name="nodelist" select="item"/> 
      </xsl:call-template> 
     </table> 
    </xsl:template> 

    <xsl:template name="make-columns"> 
     <xsl:param name="nodelist"/> 
     <xsl:param name="columns-number" select="4"/> 

     <tr> 
      <xsl:apply-templates select="$nodelist[ 
          not(position() > $columns-number) 
          ]"/> 
      <xsl:if test="count($nodelist) &lt; $columns-number"> 
       <xsl:call-template name="empty-cells"> 
        <xsl:with-param name="finish" select="$columns-number - count($nodelist)"/> 
       </xsl:call-template> 
      </xsl:if> 
     </tr> 

     <!-- If some nodes are left, recursively call current 
     template, passing only nodes that are left --> 
     <xsl:if test="count($nodelist) > $columns-number"> 
      <xsl:call-template name="make-columns"> 
       <xsl:with-param name="nodelist" select="$nodelist[ 
             position() > $columns-number 
             ]"/> 
      </xsl:call-template> 
     </xsl:if> 
    </xsl:template> 

    <xsl:template match="item"> 
     <td> 
      <xsl:apply-templates/> 
     </td> 
    </xsl:template> 

    <xsl:template name="empty-cells"> 
     <xsl:param name="finish"/> 
     <td/> 
     <xsl:if test="not($finish = 1)"> 
      <xsl:call-template name="empty-cells"> 
       <xsl:with-param name="finish" select="$finish - 1"/> 
      </xsl:call-template> 
     </xsl:if> 
    </xsl:template> 

</xsl:stylesheet> 
+0

Bạn có thể chỉnh sửa nó như thế nào để đệm hàng cuối cùng với các phần tử rỗng sao cho mỗi hàng chứa 4 ô? –

+0

@ Jason S, một lúc. – Flack

+0

@ Jason S, kiểm tra chỉnh sửa của tôi. – Flack

5

I. XSLT giải pháp 1.0:

Đây có lẽ là một trong những giải pháp ngắn nhất có thể mà đáng chú ý là không đòi hỏi đệ quy rõ ràng:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:param name="pNumCols" select="4"/> 

<xsl:template match="/*"> 
    <table> 
    <xsl:apply-templates select="*[position() mod $pNumCols =1]"/> 
    </table> 
</xsl:template> 

<xsl:template match="item"> 
    <tr> 
    <xsl:apply-templates mode="copy" select= 
    ". | following-sibling::*[not(position() >= $pNumCols)]"/> 
    </tr> 
</xsl:template> 

<xsl:template match="item" mode="copy"> 
    <td><xsl:value-of select="."/></td> 
</xsl:template> 
</xsl:stylesheet> 

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

<items> 
    <item>1</item> 
    <item>2</item> 
    <item>3</item> 
    <item>4</item> 
    <item>5</item> 
    <item>6</item> 
    <item>7</item> 
    <item>8</item> 
    <item>9</item> 
    <item>10</item> 
    <item>11</item> 
    <item>12</item> 
    <item>13</item> 
    <item>14</item> 
    <item>15</item> 
    <item>16</item> 
    <item>17</item> 
    <item>18</item> 
    <item>19</item> 
    <item>20</item> 
    <item>21</item> 
    <item>22</item> 
    <item>23</item> 
    <item>24</item> 
    <item>25</item> 
    <item>26</item> 
    <item>27</item> 
</items> 

sự muốn, kết quả chính xác được sản xuất:

<table> 
    <tr> 
     <td>1</td> 
     <td>2</td> 
     <td>3</td> 
     <td>4</td> 
    </tr> 
    <tr> 
     <td>5</td> 
     <td>6</td> 
     <td>7</td> 
     <td>8</td> 
    </tr> 
    <tr> 
     <td>9</td> 
     <td>10</td> 
     <td>11</td> 
     <td>12</td> 
    </tr> 
    <tr> 
     <td>13</td> 
     <td>14</td> 
     <td>15</td> 
     <td>16</td> 
    </tr> 
    <tr> 
     <td>17</td> 
     <td>18</td> 
     <td>19</td> 
     <td>20</td> 
    </tr> 
    <tr> 
     <td>21</td> 
     <td>22</td> 
     <td>23</td> 
     <td>24</td> 
    </tr> 
    <tr> 
     <td>25</td> 
     <td>26</td> 
     <td>27</td> 
    </tr> 
</table> 

Giải thích:

  1. Số truy nã của các tế bào mỗi hàng được quy định trong bên ngoài/toàn cầu thông số$pNumCols.

  2. Templates chỉ được áp dụng đối với trẻ em như vậy yếu tố hàng đầu, có vị trí là sự bắt đầu của một hàng mới - họ được tạo ra bởi sự biểu hiện $k * $pNumCols +1, trong đó $ k thể được bất kỳ số nguyên.

  3. Mẫu rằng việc xử lý từng hạng mục hàng-bắt đầu tạo ra một hàng (tr phần tử) và bên trong nó áp dụng mẫu trong một chế độ đặc biệt "copy" cho $pNumCols bắt đầu với chính nó.

  4. Mẫu phù hợp với một item trong chế độ "copy" chỉ đơn giản là tạo ra một tế bào (td phần tử) và đầu ra bên trong nó chuỗi giá trị của các yếu tố item bị phù hợp.

II. giải pháp XSLT 2.0:

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output omit-xml-declaration="yes" indent="yes"/> 
    <xsl:param name="pNumCols" select="4"/> 

    <xsl:template match="items"> 
     <table> 
      <xsl:for-each-group select="item" 
      group-by="(position()-1) idiv $pNumCols"> 
       <tr> 
        <xsl:for-each select="current-group()"> 
         <td> 
          <xsl:apply-templates/> 
         </td> 
        </xsl:for-each> 
       </tr> 
      </xsl:for-each-group> 
     </table> 
    </xsl:template> 
</xsl:stylesheet> 

áp dụng trên tài liệu XML giống như trước, sự biến đổi này tạo ra giống nhau, kết quả chính xác.

Giải thích:

  1. Các hướng dẫn <xsl:for-each-group> được sử dụng để chọn các nhóm khác nhau của item yếu tố trong đó mỗi nhóm chứa các yếu tố đó phải được thể hiện trong một hàng.

  2. Toán tử XPath 2.0 chuẩn idiv được sử dụng cho mục đích này.

  3. Hàm XSLT 2.0 current-group() chứa tất cả các mục phải được trình bày trong hàng hiện tại.

+0

+1. Rất thanh lịch. – Flack

+0

+1 Giải pháp tốt. –

+1

Đã lưu ngày của tôi. Cảm ơn, bạn đời. – Rolf

0

Với for-each nhóm bạn có thể có được một giải pháp thanh lịch hơn:

<xsl:template match="items"> 
    <table> 
    <xsl:for-each-group select="item" group-by="ceiling(position() div $column_width)"> 
     <tr> 
     <xsl:for-each select="current-group()"> 
      <td> 
      <xsl:apply-templates/> 
      </td> 
     </xsl:for-each> 
     </tr> 
    </xsl:for-each-group> 
    </table> 
</xsl:template> 
+0

điều này tạo ra một bảng với bốn hàng, không phải là một bảng với 4 cột! –

+0

Cảm ơn bạn.Thay thế một mod b bằng trần (a div b) – AtnNn

+0

Có một toán tử 'idiv' (phân chia số nguyên) trong XPath/XSLT 2.0 –

1

Chỉ cần cho phong cách, đây XSLT 1.0 stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:param name="pColumns" select="4"/> 
    <xsl:template match="/*"> 
     <table> 
      <xsl:apply-templates select="*[position() mod $pColumns = 1]"/> 
     </table> 
    </xsl:template> 
    <xsl:template match="item"> 
     <xsl:variable name="vItems" 
         select=".|following-sibling::*[$pColumns > position()]"/> 
     <tr> 
      <xsl:apply-templates select="$vItems" mode="makeCell"/> 
      <xsl:call-template name="fillRow"> 
       <xsl:with-param name="pItems" 
           select="$pColumns - count($vItems)"/> 
      </xsl:call-template> 
     </tr> 
    </xsl:template> 
    <xsl:template match="item" mode="makeCell"> 
     <td> 
      <xsl:value-of select="."/> 
     </td> 
    </xsl:template> 
    <xsl:template name="fillRow"> 
     <xsl:param name="pItems" select="0"/> 
     <xsl:if test="$pItems"> 
      <td/> 
      <xsl:call-template name="fillRow"> 
       <xsl:with-param name="pItems" select="$pItems - 1"/> 
      </xsl:call-template> 
     </xsl:if> 
    </xsl:template> 
</xsl:stylesheet> 

Với @ câu trả lời đầu vào Flack của, đầu ra:

<table> 
    <tr> 
     <td>1</td> 
     <td>2</td> 
     <td>3</td> 
     <td>4</td> 
    </tr> 
    <tr> 
     <td>5</td> 
     <td>6</td> 
     <td>7</td> 
     <td>8</td> 
    </tr> 
    <tr> 
     <td>9</td> 
     <td>10</td> 
     <td>11</td> 
     <td>12</td> 
    </tr> 
    <tr> 
     <td>13</td> 
     <td>14</td> 
     <td>15</td> 
     <td>16</td> 
    </tr> 
    <tr> 
     <td>17</td> 
     <td>18</td> 
     <td>19</td> 
     <td>20</td> 
    </tr> 
    <tr> 
     <td>21</td> 
     <td>22</td> 
     <td>23</td> 
     <td>24</td> 
    </tr> 
    <tr> 
     <td>25</td> 
     <td>26</td> 
     <td>27</td> 
     <td /> 
    </tr> 
</table> 
+0

+1. Đẹp quá. – Flack

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