XQuery/HTML Table View
Motivation
editWhen we have an XML file with uniform tabular structure, with no repeated or compound children, a generic table view is handy. We can use the XML element names as the column headers.
Sequence as HTML Table
editHere is a function which achieves this. It takes a sequence of elements and creates an HTML table, one row per node in the sequence. It uses a little introspection with the name() function to get the names of the children of the first node to form the column headings. For each node, the children is accessed by node name so that there is no requirement that all elements are present or in the same order as in the first node.
xquery version "1.0";
declare function local:sequence-to-table($seq) {
(: assumes all items in $seq have the same simple element structure determined by the structure of the first item :)
<table border="1">
<thead>
<tr>
{for $node in $seq[1]/*
return <th>{name($node)}</th>
}
</tr>
</thead>
{for $row in $seq
return
<tr>
{for $node in $seq[1]/*
let $data := string($row/*[name(.)=name($node)])
return <td>{$data}</td>
}
</tr>
}
</table>
};
Example
editGiven XML with a uniform tabular structure
the following script will create the HTML table:
xquery version "1.0";
declare option exist:serialize "method=xhtml media-type=text/html";
local:sequence-to-table ( ) {.. };
let $data := doc("furniture.xml")/*
return local:sequence-to-table($data/*)
Discussion
editThis approach is ideal if you know that the first node in a dataset has all the elements for all the columns in the table.
This approach is used in the later Database example to display computed sequences.
The following line must be added if you are using strict XHTML. This puts all the HTML tags (<table>, <htead>, <th>, <tbody>, <tr> and <td>) in the correct namespace.
declare base-uri "http://www.w3.org/1999/xhtml";
Sequence as CSV
editA similar approach can be used to export the sequence as CSV.
xquery version "1.0";
declare option exist:serialize "method=text media-type=text/text";
declare variable $sep := ',';
declare variable $eol := ' ';
declare function local:sequence-to-csv($seq) as xs:string {
(: returns a multi-line string of comma delimited strings :)
string-join(
(string-join($seq[1]/*/name(.),$sep),
for $row in $seq
return
string-join(
for $node in $seq[1]/*
let $data := string($row/*[name(.)=name($node)])
return
if (contains($data,$sep))
then concat('"',$data,'"')
else $data
, $sep)
),$eol )
};
let $xml := doc("furniture.xml")/*
return local:sequence-to-csv($xml/*)
Discussion
editTo save the file directly from the browser to a named file for loading intol a spreadsheet, add :
let $header := response:set-header("content-disposition","attachment; filename=furniture.csv")