ns_xml
2.0 Requirements and Design
ns_xml
as it exists currently is an integral part of OpenACS 4.x, but its implementation
fails to expose several important aspects of libxml
's tree manipulation API.
As a result, it is impossible to create some legal XML documents using
ns_xml
. The shortcoming lies in ns_xml
's inability
to create node content through the use of text
nodes. In
ns_xml
, the only way to manipulate the content of a node is to
either initialize the node's content at creation time (e.g. through a
node new_child
call) or by setting it explicitly through
node setcontent
. That overlooks the case of this simple XML
snippet:
<sentence>Here is <keyword>my</keyword> sentence</sentence>
That is only part of the challenge, however. ns_xml
is also
limited to the linear creation of a document. This is great if your goal is
to programmatically serialize data in a database, which is the obvious use
of ns_xml
within OpenACS. If you hope to mutate the structure of
an existing document or create a document in a random-access fashion (e.g. through a
user interface), however, you're in trouble. The following shortcomings exist:
ns_xml
1.x) Tcl code.text
node creation.ns_xml doc free
naming convention. This is Tcl. We don't want to think about freeing memory. We're just deleting persistent documents here. :)
Creating this functionality means adding several commands to the
ns_xml
API. The naming scheme of ns_xml
's existing
calls is very intuitive to the Tcl developer. However, it quickly became
apparent that the naming scheme wasn't general enough to encompass the new
functionality cleanly. If an attempt were made to add the functionality
described above within the existing naming scheme, all transparency would be
lost. Tcl developers would find themselves referring to reference material
constantly. Luckily, the naming scheme proposed below does not conflict with
the existing scheme (except in cases where the commands are identical between
the two schemes).
Thus, all the deprecated calls can be mapped to their 2.0
equivalents, and a user can run legacy Tcl code unmodified.
In the new scheme, commands are divided into four functional buckets which largely coincide with their first word. All calls follow the grammatical convention:
with the sole exception ofsubject verb [object]
ns_xml create xml
, which is unique in
that its job is to conjure up an object with no relationship to anything that
exists.
Note that the Node Interaction bucket is subdivided into several categories due to its complexity.
set xml_doc_id [ns_xml string parse xml ?-persist? ?-validate? string]
set xsl_doc_id [ns_xml string parse xsl ?-persist? ?-validate? string]
set xml_doc_id [ns_xml create xml ?-persist? ?doc-version?]
set new_xml_doc_id [ns_xml transform ?-persist? xml_doc_id xsl_doc_id]
set node_id [ns_xml doc get root doc_id]
set node_id [ns_xml doc create root doc_id node_name node_content]
set node_id [ns_xml doc render doc_id]
set node_id [ns_xml doc delete doc_id]
set node_id [ns_xml doc cleanup doc_id]
(tolerant of already deleted documents)set node_id_list [ns_xml node get children node_id]
set node_id [ns_xml node get parent node_id]
set node_id [ns_xml node create child_node node_id name content]
set node_id [ns_xml node create child_text node_idcontent]
set node_id [ns_xml node create prev_sibling_node node_id name content]
set node_id [ns_xml node create prev_sibling_text node_id content]
set node_id [ns_xml node create next_sibling_node node_id name content]
set node_id [ns_xml node create next_sibling_text node_idcontent]
new_node_id [ns_xml node clone node_id]
set new_node_id [ns_xml node relink_as child node_id new_parent_node_id]
set new_node_id [ns_xml node relink_as prev_sibling node_id new_sibling_node_id]
set new_node_id [ns_xml node relink_as next_sibling node_id new_prev_sibling_node_id]
ns_xml node delete node_id
set string [ns_xml node get attr node_id prop_name]
set string [ns_xml node get name node_id prop_name]
set string [ns_xml node get type node_id prop_name]
set string [ns_xml node get content node_id prop_name]
ns_xml node set attr node_id prop_name string
ns_xml node unset attr node_id prop_name
ns_xml node set content node_id string
set string [ns_xml node render node_id]
ns_xml string parse
and not just ns_xml parse
?
Because future support may be added for parsing filehandles in addition to tcl in-memory strings.
ns_xml create xml
and not just ns_xml create
?
This leaves the door open for dynamic creation of other documents like XSL or DTDs in the future. Note that XSL can currently only be parsed and applied to XML documents as-is.
ns_xml node create child
and ns_xml node create next_sibling
when you can
just create a new child of a given node's parent? (or similar questions)
This API walks a fine line between developer usability and cleanliness. The number of
calls could have been drastically reduced as well if we offered a lower-level API requiring developers to
first instantiate nodes and then link them as they chose. But that makes the developer's life
harder (not to mention decreasing performance by putting code that would be in C in Tcl), which is what we're trying to avoid.
We're also trying to get a fair amount of interoperability with the 1.x API, whose lack of a get parent
command mandated the use of the create next_sibling
command in cases where the parent was inconvenient or
impossible to derive. So deprecating the whole create sibling
concept seemed a bit harsh,
especially since it's speedier than the Tcl workaround.
Because the old ordering goes against every other command in the API
xml node render
command? You could clone the node, relink the copy to a new document and render that!
Partly because it creates parallelism with the document itself in that a node can be created, futzed with, serialized and deleted. Partly because it's more efficient than the alternative. Partly because it's useful in creating a nice UI for editing complicated XML documents. Partly because I say so. ;-)