Using XSLT to create a well formatted nested table from unknown XML

时间:2015-07-13 20:59:48

标签: php xml xslt dom xslt-1.0

The task I was given: My boss wants me to create an XSLT that takes XML of an unknown structure, and put it into a nested table (tag names as table headers) without repeating table headers where possible. I was almost able to get what he wants (The table )

The question: I have only worked with XSLT's for a few days now (have gone through a few tutorials and played with it a bit), So if anyone can point me in a good direction on where to find information that will help me with my problem, it would be greatly appreciated.

Environment Data: I am working with XSLT and PHP (DOM objects). My boss wants me to become the companies XSLT expert, so if it is possible through pure XSLT, it would be appreciated.

Additional info: At the request of the people responding, additional info (code) is below.The task is to turn something like the XML snippet below into the table below for displaying. Unfortunately, the code I had is in a state of flux at the moment, so I will not be posting it (if the question is still open when I get it stable again, I will post it).

XML Snippet:

<root>
    <request>
        <details>
            <columnname>name1</columnname>
            <operator></operator>
            <value>val</value>
            <seq>1</seq>
        </details>
    </request>
    <request>
        <details>
            <columnname>name2</columnname>
            <operator>OP</operator>
            <value>val</value>
            <seq>2</seq>
        </details>
    </request>
    <request>
        <details>
            <columnname>name3</columnname>
            <value>val</value>
            <seq>3</seq>
        </details>
    </request>
    <response>
        <details>
            <columnname>name4</columnname>
            <value>val</value>
            <seq>4</seq>
        </details>
    </response>
</root>

Desired Output

<table border="1" style="border-collapse:collapse;width:100%">
    <tr>
        <th>root</th>
    </tr>
    <tr>
        <td>
            <table border="1" style="border-collapse:collapse;width:100%">
                <tr>
                    <th>request</th>
                    <th>response</th>
                </tr>
                <tr>
                    <td>
                        <table border="1" style="border-collapse:collapse;width:100%">
                            <tr>
                                <th>details</th>
                            </tr>
                            <tr>
                                <td>
                                    <table border="1" style="border-collapse:collapse;width:100%">
                                        <tr>
                                            <th>columnname</th>
                                            <th>operator</th>
                                            <th>value</th>
                                            <th>seq</th>
                                        </tr>
                                        <tr>
                                            <td>name1</td>
                                            <td></td>
                                            <td>val</td>
                                            <td>1</td>
                                        </tr>
                                        <tr>
                                            <td>name2</td>
                                            <td>OP</td>
                                            <td>val</td>
                                            <td>2</td>
                                        </tr>
                                        <tr>
                                            <td>name3</td>
                                            <td></td>
                                            <td>val</td>
                                            <td>3</td>
                                        </tr>
                                    </table>
                                </td>
                            </tr>
                        </table>
                    </td>
                    <td>
                        <table border="1" style="border-collapse:collapse;width:100%">
                            <tr>
                                <th>details</th>
                            </tr>
                            <tr>
                                <td>
                                    <table border="1" style="border-collapse:collapse;width:100%">
                                        <tr>
                                            <th>columnname</th>
                                            <th>value</th>
                                            <th>seq</th>
                                        </tr>
                                        <tr>
                                            <td>name4</td>
                                            <td>val</td>
                                            <td>4</td>
                                        </tr>
                                    </table>
                                </td>
                            </tr>
                        </table>
                    </td>
                </tr>
            </table>
        </td>
    </tr>
</table>

Response to michael.hor257k: It would look something like this.

<table border="1" style="border-collapse:collapse;width:100%">
    <tr>
        <th>root</th>
    </tr>
    <tr>
        <td>
            <table border="1" style="border-collapse:collapse;width:100%">
                <tr>
                    <th>parent</th>
                    <th>uncle</th>
                </tr>
                <tr>
                    <td>
                        <table border="1" style="border-collapse:collapse;width:100%">
                            <tr>
                                <th>child</th>
                                <th>nephew</th>
                            </tr>
                            <tr>
                                <td>
                                    <table border="1" style="border-collapse:collapse;width:100%">
                                        <tr>
                                            <th>string</th>
                                            <th>number</th>
                                            <th>grandchild</th>
                                            <th>date</th>
                                        </tr>
                                        <tr>
                                            <td>A</td>
                                            <td></td>
                                            <td></td>
                                            <td></td>
                                        </tr>
                                        <tr>
                                            <td>B</td>
                                            <td></td>
                                            <td></td>
                                            <td></td>
                                        </tr>
                                        <tr>
                                            <td>C</td>
                                            <td>1</td>
                                            <td>
                                                <table border="1" style="border-collapse:collapse;width:100%">
                                                    <tr>
                                                        <th>string</th>
                                                        <th>substring</th>
                                                        <th>number</th>
                                                    </tr>
                                                    <tr>
                                                        <td>DD</td>
                                                        <td>EE</td>
                                                        <td>33</td>
                                                    </tr>
                                                </table>
                                            </td>
                                            <td></td>
                                        </tr>
                                        <tr>
                                            <td></td>
                                            <td>2</td>
                                            <td></td>
                                            <td></td>
                                        </tr>
                                        <tr>
                                            <td>F</td>
                                            <td></td>
                                            <td></td>
                                            <td>2015-02-12</td>
                                        </tr>
                                    </table>
                                </td>
                                <td>
                                    <table border="1" style="border-collapse:collapse;width:100%">
                                        <tr>
                                            <th>string</th>
                                        </tr>
                                        <tr>
                                            <td>G</td>
                                        </tr>
                                    </table>
                                </td>
                            </tr>
                        </table>
                    </td>
                    <td>
                        <table border="1" style="border-collapse:collapse;width:100%">
                            <tr>
                                <th>niece</th>
                            </tr>
                            <tr>
                                <td>
                                    <table border="1" style="border-collapse:collapse;width:100%">
                                        <tr>
                                            <th>string</th>
                                        </tr>
                                        <tr>
                                            <td>H</td>
                                        </tr>
                                    </table>
                                </td>
                            </tr>
                        </table>
                    </td>
                </tr>
            </table>
        </td>
    </tr>
</table>

1 个答案:

答案 0 :(得分:1)

正如我在评论中所说,这并不简单。基本上,您希望删除具有相同路径的任何重复节点(其中仅使用节点名称计算路径)并显示生成的层次结构。

这需要进行预处理,以便为所有节点分配路径。我们还需要为每个节点提供其父路径 - 以便稍后可以通过其新父节点(以前可能是它的叔叔或叔叔或......)来调用它。

在第二步也是最后一步,我们将Muenchian grouping应用于第一遍的结果,只留下不同的路径。

在这个例子中,我将只处理元素,结果将显示为无序列表。

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="node-by-path" match="node" use="@path" />
<xsl:key name="node-by-parent-path" match="node" use="@parent-path" />

<xsl:template match="/">
    <!-- first-pass -->
    <xsl:variable name="first-pass">
        <xsl:apply-templates select="*" mode="firstpass"/>
    </xsl:variable>
    <!-- output -->
    <ul>
        <xsl:apply-templates select="exsl:node-set($first-pass)/node[@parent-path='']" />
    </ul>
</xsl:template>

<xsl:template match="*" mode="firstpass">
    <xsl:variable name="parent-path">
        <xsl:for-each select="ancestor::*">
            <xsl:value-of select="concat('/', name())"/>
        </xsl:for-each>                 
    </xsl:variable> 
    <node name="{name()}" parent-path="{$parent-path}" path="{concat($parent-path, '/', name())}">
        <xsl:apply-templates select="*" mode="firstpass"/>
    </node>
</xsl:template>

<xsl:template match="node">
    <li>
        <xsl:value-of select="@name"/>
        <xsl:variable name="next" select="key('node-by-parent-path', @path)" />
        <xsl:if test="$next">
            <ul>
                <xsl:apply-templates select="$next[count(. | key('node-by-path', @path)[1]) = 1]"/>
            </ul>
        </xsl:if>
    </li>
</xsl:template> 

</xsl:stylesheet>

测试输入XML

<root>
   <parent>
      <child>
         <string>A</string>
         <string>B</string>
      </child>
      <child>
         <string>C</string>
         <number>1</number>
         <number>2</number>
         <grandchild>
            <string>DD</string>
            <substring>EE</substring>
            <number>33</number>
         </grandchild>
      </child>
   </parent>
   <parent>
      <child>
         <string>F</string>
         <date>2015-02-12</date>
      </child>
      <nephew>
         <string>G</string>
      </nephew>
   </parent>
   <uncle>
      <niece>
         <string>H</string>
      </niece>
   </uncle>
</root>

<强>结果

<?xml version="1.0" encoding="UTF-8"?>
<ul>
   <li>root<ul>
         <li>parent<ul>
               <li>child<ul>
                     <li>string</li>
                     <li>number</li>
                     <li>grandchild<ul>
                           <li>string</li>
                           <li>substring</li>
                           <li>number</li>
                        </ul>
                     </li>
                     <li>date</li>
                  </ul>
               </li>
               <li>nephew<ul>
                     <li>string</li>
                  </ul>
               </li>
            </ul>
         </li>
         <li>uncle<ul>
               <li>niece<ul>
                     <li>string</li>
                  </ul>
               </li>
            </ul>
         </li>
      </ul>
   </li>
</ul>

<强>渲染

enter image description here