letzte Aktualisierung: 21. März 2007
default ] [ fancy ]

factis open source software

Unix/Linux: foss-0.1.tgz
David Leuschner <david at loisch.de>
Stefan Wehr <mail at stefanwehr.de>
Johannes Weiss <weissi at tux4u.de>


db2xml is an XML based interpreted language used to generate XML documents by querying a relational database system using SQL.

Why implement something like that?

Many web pages that need dynamic content use languages like Perl, PHP, ASP or JSP. The problem with this approach is that there is no separation between (query or application) logic, static content and presentation logic and style which in many environments are developed by different people (programmers, content editors and web designers). Apart from that writing complex SQL queries in Java, Perl or PHP is very cumbersome because of the quoting. Mixing three or even more languages (PHP,SQL,HTML,CSS,JavaScript) in one file isn't beautiful either.

With db2xml you can define a mapping between SQL query results and an XML structures to generate arbitrary XML documents containing database query results. A typical web application would proceed transforming this data using XSLT to HTML pages. This setup separates query and application logic (the db2xml script) from the web developers job of implementing the presentation. If you want to go further static content (written by a third person) may be incorporated during the XSLT processing.

But not only typical web applications may benefit from using db2xml: many business to business interfaces use XML as a data exchange format. With db2xml it's very easy to export data from your corporate database as XML documents.


nested loop

<?xml version="1.0" encoding="ISO-8859-1"?> <dx:doc xmlns:dx="http://www.loisch.de/ns/db2xml/lang" xmlns:d="http://www.loisch.de/ns/db2xml/data"> <dx:main> <link-database> <dx:for-each> <dx:query> SELECT catid, catname, catdesc FROM category </dx:query> <dx:rows> <category id="{$catid}"> <name><dx:value-of expr="$catname"/></name> <desc><dx:value-of expr="$catdesc"/></name> <links> <dx:for-each> <dx:query> SELECT linkname, linkdesc, linkurl FROM link WHERE linkcat={$catid} <dx:query> <link> <dx:data-pattern prefix="link"> <d:name/> <d:desc/> <d:url/> </dx:data-pattern> </link> </dx:for-each> </links> </category> </dx:rows> </dx:for-each> </link-database> </dx:main> </dx:doc>
<?xml version="1.0" encoding="ISO-8859-1"?> <link-database> <category id="1"> <name>Personal</name> <desc>Personal websites of people I know.</desc> <links> <link> <name>David</name> <desc>David Leuschners personal website</desc> <url>http://www.loisch.de</url> </link> <link> <name>Stefan</name> <desc>Stefan Wehrs personal website</desc> <url>http://www.stefanwehr.de</url> </link> </links> </category> <category id="2"> <name>Projects</name> <desc>interesting open source software projects</desc> <links> <link> <name>Python</name> <url>http://www.python.org</url> </link> </link> </category> </link-database>

The example should be fairly self explaining. If it's not please drop me a line so that I can add some documentation! ;-)


xml2db is the counterpart to db2xml for applications that need to process XML documents by writing data into a relational database system while optionally generating an XML or plain text output document. The python like language features tight XPath and SQL integration and a sophisticated constraints system for validity checking as well as general programming language features such as loops, functions and exceptions.


nested loop

constraint str64 is StringConstraint with min := 1 max := 64 regex := "\w+" end constraint non_empty_str is StringConstraint with min := 1 end fun main() { for $link in {//link} { $cat = {ancestor::category/name}; $catid = [[ SELECT catid FROM category WHERE catname={$cat}]]; expect {name} matches str64 constraint else raise LINK_INVALID, format("Invalid link name: %s", {name}); $desc = {desc} is non_empty_str else use None; [[ INSERT INTO link (linkcat, linkname, linkdesc) VALUES ({$catid}, {name}, {$desc}) ]] } }


Pycoon is yet another framework for web application development. It is written in the Python programming language and borrows several design ideas from Apache Cocoon - hence the name. It is designed to be integrated into standard webserver software. An interface to Apache using mod_python is provided with the distribution.

Pycoon allows the application developer to compose a web application of standard and self written actions and request handlers described in a configuration file called sitemap which also happens to be valid python code. The sitemap essentially is just a list of matchers and associated actions that are executed if a request is matched. Unlike Cocoon these associated actions must not form an xml pipeline (although they might if this is what best suites your needs) but communicate using a per request data-structure called the object model (often abbreviated as om).


def sitemap(): return [ strmatch("hello", send(str("Hello World!"))), globmatch("sendfile/*", sendfile(fname("$1")), globmatch("send/*", send(read(fname("$1")))) ]

This very simple sitemap contains three matcher instances, one strmatch and two globmatch instance which are associated with three action instances. A sitemap is always evaluated for a specific request context. For the main sitemap this context consists of all URLs the Pycoon instance is responsible for. For example consider you have configured Pycoon to handle every webserver request for which the URL starts with http://www.myserver.com/~myaccount/pycoon/ then this URL is the context URL. Matchers ignore this context url and consider only the suffix of the actual URL stripping of the context URL prefix. Thus the strmatch matcher instance from the example exactly matches the URL http://www.myserver.com/~myaccount/pycoon/hello. If it matches the associated send action instance is executed. The send action sends its at runtime evaluated parameter value to the client. In the example this is just a constant string value str("Hello World!").

So far three sitemap components where mentioned: matchers, actions and values. Matchers are responsible to examine the current request and decide if the associated actions should be executed or if other matchers should be tried. Actions perform some serverside action like setting response headers, reading a post request body or performing database queries. Most actions take parameters that provide values. These values might be constant but may also refer to local files, request parameters or sitemap variables.

The second matcher instance in the example is a globmatch matcher. It matches URLs conforming to the given pattern. "send/*" matches suffixes like "send/myfile.txt" but not "send/subdir/file". The associated sendfile action sends the contents of a local file to the client. It's parameter must evaluate to an absolute filename. The fname value takes a filename pattern relative to the directory containing the sitemap, expands this pattern, calculates the absolute path and checks that the specified file exists. As you might have guessed $1 is not a filename but a pattern that it expanded. During globmatch (or rematch) matching positional parameters are set. For the globmatch matcher the first * or ** pattern becomes $1, the second $2 and so on. So if the URL http://www.myserver.com/~myaccount/pycoon/sendfile/myfile.txt was requested the second matcher would match, try to read the file myfile.txt from the directory where the sitemap resides and send the file contents to the client.

Finally the third matcher instance again makes use of the send action to accomplish the same result: to send file contents to the client. As send sends the string it's parameter evaluates to we just need to provide a value that evaluates to the contents of a file: the read value.