Working with XML Data in ArsDigita Community System
by Karl Goldstein
Perspective
The ArsDigita philosophy is not to put stuff into our toolkit
that is part of the core Oracle RDBMS. Oracle Corporation has thousands of
programmers, their most reliable product is the core RDBMS server, they have
documentation, training, support, etc. Oracle has wonderful support for XML
built into their core RDBMS from version 8.1.5 forward. This support is
available to any Oracle client, including an AOLserver running the ArsDigita
Community System. The XML support is made possible by the fact that the core
Oracle server is capable of running programs in procedural languages such as
Java and PL/SQL.
Basically what we do for a site that needs to make heavy use of XML is to
download the appropriate Java libraries for Oracle's built-in Java VM.
After that it is just a question of how to use those libraries:
I want to grab some data from an Oracle table and publish it in
XML: run the xmlgen.getXML procedure inside Oracle,
which will automatically figure out how to go from the Oracle data dictionary
into XML; AOLserver gets a big string of XML back from Oracle
I want to grab some data from a server elsewhere on the Internet
and stuff it into an Oracle table: Take the entire XML document and
feed it to Oracle, then run xmlgen.insertXML.
I want to render some data stored in Oracle as XML and publish it
on the Web in HTML: Here you have an Oracle CLOB column containing
an XML document. You want a reader to be able to view it in a standard
browser. You want to use XSL to render the XML document into HTML. Do this by
calling our apply_xsl procedure (invokes some Java code within
the database).
Generally AOLserver wants to parse XML that comes from a database table. In
this case, it makes sense to do the parsing that is already in the database
Overview
This document describes how to use XML to publish and retrieve data from
Oracle tables. The procedure relies on Oracle's XML parser and SQL to XML
utility, which makes it easy to transform a query result into a simple XML
document with the following general structure:
<rowset>
<row>
<column1>value</column1>
<column2>value</column2>
...
<columnN>value</columnN>
</row>
...
</rowset>
The parser is written in Java, but Oracle provides a PL/SQL wrapper
package so that the methods can be called directly from SQL. The package
includes methods to format a query result as an XML document, and to insert
an XML document into a database table. However, you cannot specify a URL
as the document source; the document must already be stored locally in a
CLOB or varchar variable. It would be easy to write a stored procedure in
Java that performed the retrieval (the Oracle Java classes support this
form), but Oracle requires you to have special permissions to open a socket
from a Java stored procedure. We will rely on AOLserver to do the retrieval
for us.
Preparation: Load the Utility
The first step in any XML project is to load the Java classes and PL/SQL
package into your tablespace. The whole package is available from the Oracle
web site at http://technet.oracle.com/tech/xml/oracle_xsu/
. You may have to register with Oracle TechNet to get it.
Once you have managed to get the tar file onto your server, explode it and
change to the lib directory. Edit the database user and password in
the file oraclexmlsqlload.csh and run the script from the shell
command line. This will load everything and perform some tests to ensure that
it is working properly.
Example data model
For the examples below, suppose that you have this database table you want
to publish as XML:
create table xmltest (
pk integer primary key,
color varchar2(40),
shape varchar2(40)
);
insert into xmltest values (1, 'red', 'circle');
insert into xmltest values (2, 'blue', 'triangle');
insert into xmltest values (3, 'green', 'square');
commit;
Creating XML from Oracle table data
The Oracle package xmlgen allows you to publish any query result
as an XML document. As an example, we will publish the simplest possible
query:
select * from xmltest;
The Oracle package xmlgen has a getXML function that
turns a query into a simple XML document. You might hope that something like
this would work:
select xmlgen.getXML('select * from xmltest') from dual;
This works fine in SQL*plus, but only works once per session if
called from AOLserver. This probably has to do with the fact that the
function returns a temporary CLOB which has to be freed before the function
can be called again, although this doesn't really explain why it works in
SQL*plus.
The workaround is to use a temporary table, which is a new
feature in Oracle 8i that stores session- or transaction- specific
information and deletes it at the end of the session or transaction. This
table will hold the XML document CLOB long enough to get it into a Tcl
string. We will use the on commit delete rows option (this is the
default) so that any rows inserted during a transaction are deleted at the
end of the transaction.
First you have a create a table to store the generated XML documents. Here
is a skeleton table, although you may want to extend it to suit your
needs:
create sequence xmldoc_seq start with 1;
create global temporary table xmldocs (
doc_id integer primary key,
doc CLOB
) on commit delete rows;
Next, you need a PL/SQL wrapper function that generates the XML document
into the temporary CLOB, stores it, and returns the id of the stored
document:
create or replace function get_xml (
query varchar2)
return integer is
doc_id integer;
begin
select xmldoc_seq.nextval into doc_id from dual;
insert into xmldocs values (doc_id, xmlgen.getXML(query));
return doc_id;
end;
/
show errors;
Publish an XML document from Oracle data
To actually publish the query as an XML document, create an AOLserver tcl
page called xmltest-publish:
db_transaction {
set doc_id [ns_ora exec_plsql $db "
begin
:1 := get_xml('select * from xmltest');
end;
"]
set result [ns_db 1row $db "select doc from xmldocs where doc_id = $doc_id"]
set xmldoc [ns_set value $result 0]
}
ns_return 200 text/plain $xmldoc
This code obtains the document ID from the get_xml function
created above, and then retrieves the actual document. Note that the
ns_ora exec_plsql procedure must be used because the function has
the side effect of inserting a row into a table. The entire block is wrapped
in a transaction so that the generated XML document is automatically deleted
once the page is written.
Retrieving an XML document and store its data in an Oracle table
To retrieve an XML document and store its field values into a database
table, create another copy of the above table named xmltest2. Then
create an AOLserver tcl page called xmltest-retrieve:
set xmldoc [ns_httpget http://yourdomain/xmltest-publish]
regsub -all "\[\r\n\]" $xmldoc {} xmldoc
set statement "
declare
rowsp integer;
begin
rowsp := xmlgen.insertXML('xmltest2', [ns_dbquotevalue $xmldoc]);
end;
"
db_dml unused $statement
ns_return 200 text/html "XML inserted."
Once the XML document is retrieved using the ns_httpget method,
all line breaks in the document must be removed to avoid breaking the SQL
statement. The insertXML function itself must be executed within a
PL/SQL block; it returns the number of rows successfully inserted.
Transforming an XML document with an XSL stylesheet
Version 2 of the Oracle XML parser supports XSL stylesheets, which provide
a convenient way to transform XML documents into HTML or any other format.
The xmlgen PL/SQL package does not provide this capability, but I
have created my own Java code to support such transformations. The code is
invoked by the apply_xsl procedure in SQL/plus. It can be found in
doc/sql/XMLPublisher in the ACS distribution.
To use the function, you need a table to store XSL stylesheets in the
database:
create sequence xsldoc_seq start with 1;
create table xsldocs (
doc_id integer primary key,
doc_name varchar2(100),
doc CLOB
);
Once you have inserted a stylesheet into the table, you can apply it to
any generated xml document. Simply generate the XML document into the
xmldocs table as above, and then call apply_xsl to apply a
transformation:
...
set doc_id [ns_ora exec_plsql $db "
begin
:1 := get_xml('select * from xmltest');
apply_xsl(:1, 'mystyle.xsl');
end;
"]
...
The apply_xsl procedure is bound to a Java stored procedure that
retrieves the document from the xmldocs temporary table the named
stylesheet from the xsldocs table. It applies the transformation to
the document and updates the xmldocs table with the transformed version of
the XML document, which can then be retrieved as before.
($Id: working-with-xml.xml,v 1.1 2001/03/13 22:59:26 ben Exp $)