<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:output method="xml" omit-xml-declaration="no"/>
<xsl:template match="@*|node()" priority="1">
<xsl:copy>
<xsl:apply-templates select="@*|node()">
<xsl:sort select="name()" data-type="text" order="ascending"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Wednesday, June 10, 2009
Sort a Whole XML Document using XSL
Tuesday, May 12, 2009
How to Get XPath to an XML Node
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:attribute name="position"><xsl:call-template name="xpath.position"></xsl:call-template</xsl:attribute>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template name="xpath.position">
<xsl:param name="node" select="."></xsl:param> <!-- default value is current node -->
<xsl:param name="path" select="''"></xsl:param> <!-- default value is empty string -->
<!-- prepend the current position to $path, save it as next.path -->
<xsl:variable name="next.path"><xsl:text>*[position()=</xsl:text><xsl:value-of select="$node/count(preceding-sibling::*)+1"/><xsl:text>]</xsl:text>
<xsl:if test="$path != ''">/</xsl:if>
<xsl:value-of select="$path"></xsl:value-of>
</xsl:variable>
<xsl:choose>
<xsl:when test="$node/parent::*"> <!-- when there is a parent node -->
<xsl:call-template name="xpath.position">
<xsl:with-param name="node" select="$node/parent::*"></xsl:with-param>
<xsl:with-param name="path" select="$next.path"></xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:text>/</xsl:text>
<xsl:value-of select="$next.path"></xsl:value-of>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Identity Transformation that Removes Empty Nodes
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()">
<xsl:if test=". != '' or ./@* != ''">
<xsl:if test="not(contains(name(), 'temp'))">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:if>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Tuesday, January 06, 2009
Oracle XML Queries and Namespaces
The answer comes from Flavio Casetta at his oraclequirks blog:
select
extractValue(content,'/path/here','xmlns="http://www.w3.org/2005/Atom"') contentname
from
table
Friday, December 19, 2008
Parsing a Document into a DomDocument in JavaScript
http://groups.google.com/group/comp.lang.javascript/browse_thread/thread/762990f37ae218a3/6e0d948e1820bf24?pli=1
I called this in my "showResponse" function called by prototype.js' Ajax.Request onSuccess event. showResponse(originalRequest) receives the original XMLHttpRequest object and you need to call Darko's code using originalRequest.
However of course it didn't work for me-- in Firefox, the script runs forever. Turns out there was actually an error parsing the doc, but there's no way to tell what it is.
Time to add error output, which is defined nicely here:
http://www.faqts.com/knowledge_base/entry/versions/index.phtml?aid=15302
Haven't finished this yet :)
Monday, October 13, 2008
Exporting Table Data as Oracle XML
SELECT dbms_xmlgen.getXml(
'SELECT *
FROM your_table_name_goes_here'
, 0
) FROM dual;
Tuesday, October 07, 2008
Looping over a Set of Nodes with PL/SQL
extract('count(/xpath/here/*)') won't work. Here's the solution:select count(*) into v_maxi from TABLE(XMLSequence(extract(p_xml,'/xpath/here')));
FOR v_i IN 0..v_maxi-1 LOOP
v_xmlfrag := p_xml.extract('/xpath/here[count(preceding-sibling::*)='||v_i||']');
v_text := v_xmlfrag.extract('/record/stuff/text()').getStringVal();
END LOOP;
Insert, Append, or Update an XML element to an XML document using Oracle PL/SQL
select
insertchildxml(v_xmltype,
'/my/xpath',
lower('elementname'),
XMLType('stuff '))
from dual;
Gotchas: You must use a select statement. Argument 3 must match the element inside argument 4.
If you are modifying attributes, you can use updatexml-- but it is obnoxious because if the attribute doesn't exist, updatexml fails silently. So, if unsure, you need to user insertxml to add the attribute and if it throws an exception, then attempt an update. Like so:
BEGIN
select insertchildxml(v_xml, '//myxpath', '@myattribute',
v_newvalue) into v_xml from dual;
EXCEPTION -- if @myattribute attribute exists, insert will fail, so update instead
WHEN OTHERS THEN
select updatexml(v_xml, '//myxpath/@myattribute', v_newvalue)
into v_xml from dual;
END;
Extending DBMS.XMLSave to Insert into Multiple Tables
I've written a procedure which wraps Oracle's format inside some higher level XML elements to allow insertion into multiple tables. My solution for sequences and functions is not very flexible, but it will work for specific applications, and I can make some recommendations on how to improve the flexibility.
Let's start with inserting into multiple tables. If you look at my previous post, you will see Oracle's format for the DBMS.XMLSave function. I wrap it in two higher level elements, records (the new root) and the record element which contains a rowset.
<records>
<record table="tablenamegoeshere">
<rowset>
<row>
<columnname>value</columnname>
</row>
</rowset>
</record>
<records>
A stored procedure to handle these XML files must loop through each record and call Oracle's XMLSave on the rowset contained within. Each record element has an attribute, table, which specifies which table to insert into.
The next step is to implement sequences. We could take a cue from Hibernate and add an attribute to the column that uses a sequence. I didn't have time to implement this, so what I've done is update one column in each row with the results of an execute immediate. This assumes that there is only one sequence that needs updating and that it's name & and the containing column's name is based on the table attribute in the record element.
I use a similar procedure for function calls-- use XPath to update columns. The structure of the data is very rigid unfortunately because of this.
One gotcha -- make sure rowset and row elements are LOWER CASE. Oracle's procedures are case sensitive.
create or replace procedure PARSE_RECORD_XML(p_xml in xmltype, p_xmlid in integer) is
v_i INTEGER;
v_j INTEGER;
v_func_arg INTEGER;
v_seqnum INTEGER;
v_func_result VARCHAR2(50);
v_xmlfrag XMLTYPE;
v_tablename VARCHAR2(50);
v_seqname VARCHAR2(100);
v_maxi INTEGER;
v_maxj INTEGER;
begin
select count(*) into v_maxi from TABLE(XMLSequence(extract(p_xml,'/records/record')));
FOR v_i IN 0..v_maxi-1 LOOP
v_xmlfrag := p_xml.extract('/records/record[count(preceding-sibling::*)='||v_i||']');
v_tablename := v_xmlfrag.extract('/record/@table').getStringVal();
v_seqname := v_tablename || 'ID'; -- sequence name is a concatenation of table attribute and ID
select count(*) into v_maxj from TABLE(XMLSequence(extract(v_xmlfrag,'/record/rowset/row')));
FOR v_j in 0..v_maxj-1 LOOP
-- Get sequence number
execute immediate 'SELECT ' || v_seqname || '.nextval from dual' into v_seqnum;
-- Execute a function on some data
v_func_result := v_xmlfrag.extract('/record/rowset/row[count(preceding-sibling::*)='||v_j||']/func_arg/text()').getStringVal();
-- Insert the parent id
select insertchildxml(v_xmlfrag, '/record/rowset/row[count(preceding-sibling::*)='||v_j||']', 'parentid',
XMLType('' || p_xmlid || ' ')) into v_xmlfrag from dual;
-- Insert function result
select insertchildxml(v_xmlfrag, '/record/rowset/row[count(preceding-sibling::*)='||v_j||']', 'func_result',
XMLType('' || getMRN(v_patientid) || ' ')) into v_xmlfrag from dual;
-- Insert sequence id
select insertchildxml(v_xmlfrag, '/record/rowset/row[count(preceding-sibling::*)='||v_j||']', lower(v_seqname),
XMLType('<' || lower(v_seqname) || '>' || v_seqnum || '' || lower(v_seqname) || '>')) into v_xmlfrag from dual;
END LOOP;
InsertXML(v_xmlfrag.extract('/record/rowset'), v_tablename);
END LOOP;
end PARSE_RECORD_XML;
Inserting XML data into an Oracle Table
<ROWSET>
<ROW>
<COLUMNNAME>
value
</COLUMNNAME>
</ROW>
</ROWSET>
The other argument to the save function is the table name. Two gotchas: 1) by default, the element names are case sensitive. I think this is a nightmare with table column names, so luckily there is a flag to disable case sensitivity (why do developer's inflict case sensitivity on each other?) 2) The format is very barebones. Attributes are ignored and you can't use functions within the values for columns. You can only insert into one table as well. I've created some code to get around this, which is the topic of another post.
At least, though, you CAN use XSLT to transform your document to something that Oracle can handle natively.
Here is a stored procedure that inserts such an XML document:
CREATE OR REPLACE PROCEDURE InsertXML (p_xml in xmltype, p_tablename in varchar2) is
queryCtx DBMS_XMLsave.ctxType;
rowsInserted INT;
BEGIN
--Defines the query
queryCtx := SYS.DBMS_XMLSave.newContext(p_tablename);
DBMS_XMLSave.setIgnoreCase(queryCtx, 1);
rowsInserted := DBMS_XMLSave.insertXML(queryCtx,p_xml.getCLOBval());
DBMS_XMLSave.closeContext(queryCtx);
commit;
END;
Thursday, July 17, 2008
Passing XML stored in a String into Oracle Stored Procs
If your XML is not stored in a String, I don't know what to say since I tend to grab XML from a request and it comes out as a String.
Thursday, March 27, 2008
Counting the number of nodes in an XMLType
select count(*) into number_of_items from table(xmlsequence((the_xml.extract('/*/mynode', 'xmlns="http://purl.org/rss/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"') )));
XMLType extract on XML with namespaces
extract('/mynode','xmlns="http://stupidideas.net/stupid" xmlns:dumb="http://dumb.com/dumber"')
See also:
http://pbarut.blogspot.com/2006/11/xmltype-and-namespace-problem.html
Tuesday, March 11, 2008
Saving and Loading an XMLType to/from Oracle from Java
First off, you will need ojdbc14.jar, xmlparsenv2.jar, and xdb.jar. You probably already have ojdbc14.jar if you are doing Oracle work, but the other two I had to download.
Lets get our imports going.
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import oracle.jdbc.OracleTypes;
Now, you'll have to create a class, and inside it put this save function. My variable XMLdata is just a string I grabbed from the POST to this Java.
public void save() {
Connection conn = null;
CallableStatement callableStatement = null;
ResultSet rs = null;
DataSource dataSource = null;
InitialContext ctx = null;
System.out.println("[save] Starting");
try {
ctx = new InitialContext();
dataSource = (DataSource)ctx.lookup("java:comp/env/yourdatasourcename");
conn = dataSource.getConnection();
System.out.println("[save] Fetching");
callableStatement = conn.prepareCall("{call SAVE(?,?,?,?)}");
System.out.println("[save] prepare call done, setting values");
callableStatement.setInt(1, var1);
callableStatement.setString(2, var2);
callableStatement.setString(3, var3);
callableStatement.setObject(4, XMLdata); // note that I use setObject instead
rs = callableStatement.executeQuery();
System.out.println("[save] Executed the query");
System.out.println("[save] inserted XML data: "+XMLdata);
rs.close();
callableStatement.close();
conn.close();
System.out.println("[save] Done");
} catch (SQLException e) {
System.out.println("[save] Saving the form blew up with an SQLException");
e.printStackTrace();
} catch (Exception e) {
System.out.println("[save] Saving the form blew up with an Exception");
e.printStackTrace();
}
}
The SAVE stored procedure just takes an integer, two varchar2s, and an XMLtype as input. Nothing exciting here. Let's move on to the trickier part: loading.
public void load() {
Connection conn = null;
CallableStatement callableStatement = null;
ResultSet rs = null;
DataSource dataSource = null;
InitialContext ctx = null;
System.out.println("[load] Starting");
try {
ctx = new InitialContext();
System.out.println("got context");
dataSource = (DataSource)ctx.lookup("java:comp/env/yourdatasourcenamehere");
if(dataSource==null)
System.out.println("Got the datasource but it's null");
else
System.out.println("Got the datasource and it's not null");
conn = dataSource.getConnection();
System.out.println("Got the connection");
callableStatement = conn.prepareCall("{call LOAD(?,?,?,?)}");
System.out.println("[load] prepare call done, setting values");
callableStatement.setInt(1, InstanceId);
callableStatement.setString(2, ProcessName);
callableStatement.setString(3, TracingTag);
callableStatement.registerOutParameter(4, OracleTypes.OPAQUE,"SYS.XMLTYPE");
System.out.println("[load] Registered XMLType out parameter");
rs = callableStatement.executeQuery();
System.out.println("[load] Executed the query");
oracle.xdb.XMLType xml = oracle.xdb.XMLType.createXML((oracle.sql.OPAQUE)callableStatement.getObject(4));
System.out.println("[load] Got XML data: "+xml.getStringVal());
rs.close();
callableStatement.close();
conn.close();
System.out.println("[load] Done");
} catch (SQLException e) {
System.out.println("[load] Loading the form blew up with an SQLException");
e.printStackTrace();
} catch (Exception e) {
System.out.println("[load] Loading the form blew up with an Exception");
e.printStackTrace();
}
}The key to loading is to first register an opaque out parameter. There are ways to use other types, but I found that subsequent manipulations of the returned data required starting with this type. You can get an XMLType, but then you need to use the connection in Oracle's libraries, and that breaks other stuff... so on. So use this: callableStatement.registerOutParameter(4, OracleTypes.OPAQUE,"SYS.XMLTYPE");
Opaque can store all sorts of Oracle types. To actually manipulate the XML in a useful way, you have to store it in an XMLType. To do that, create a new XMLType and then, using a cast, assign the opaque variable to it. Like so:
oracle.xdb.XMLType xml = oracle.xdb.XMLType.createXML((oracle.sql.OPAQUE)callableStatement.getObject(4));
That's the quick one line version. A little more readable is the following:
Object xmlobject = callableStatement.getObject(4);
oracle.sql.OPAQUE xmlopaque= (oracle.sql.OPAQUE) xmlobject;
oracle.xdb.XMLType xml = oracle.xdb.XMLType.createXML(xmlopaque);
The nice thing about oracle.xdb.XMLType is that it behaves just like an XMLType in Oracle. So you can use the functions you're used to from PL/SQL. getStringVal() returns the string value of the XML for example.
Tuesday, October 09, 2007
SQL Query to select/update text from XML columns
select * FROM
table_with_xml w
WHERE
w.XMLCOLUMN.EXTRACT('/xpath/goes/here/text()').getstringval() = 'test';
And how to update:
UPDATE TABLE_WITH_XML
SET XMLCOLUMN = UPDATEXML(XMLCOLUMN,
'/xpath/goes/here/text()', 'a new value')
WHERE
xml_id = p_xml_id;
Monday, September 24, 2007
Quick XSL Primer
The basic building block of XSL is the xsl:template tag. It has one principal attribute, match, that takes an XPath query.
As part of the body text of the template tag, you will use xsl:apply-templates, which continues processing nodes further down in the XML tree. It has one attribute, select, which behaves like match.
In the examples below, replace 'node' with the tag you use in your xml. For example, if you have a tag called person:
<person>Guido</person>
Node is 'person'.
Here's a few basic queries:
//node
match, anywhere in the document, tags with the name node
/node
match, just at the root, tags with the name node
/node1//node2
match any tag with the name node2 that is underneath node1, which is at the root
.
select the current node
*
match all nodes at the current level of processing
/*
match any tag at the root
/*[@school='engineering']
match all nodes at the root with an attribute school that is set to equal 'engineering'
/*[name()='seanDavis']
this would be the same as //seanDavis while this example seems pointless look at the next one
/*[count(name()='voijinOklobja')=0]
this would match all tags in the root, unless there are tags with the name voijinOklobja. You should have a corresponding rule for when the count > 0 if you do this.
/person/children//*[count(*)=0]
select all leaf nodes underneath /person/children
/person/property[count(*/*)>0]
select all nodes under /person/property that have children (no guarantees that their children are leaves though)
/person/name[text()='Guido']
select all person tags which have a child tag which contains just 'Guido'.
/person[position()=last()]
select the last person tag in the root
/person[position()<last()]
select all but the last person tag in the root of the document (you might need to literally use
& l t ; [remove the spaces] rather than <)
/person[./property/*/name()='height']
select only people who have a property named height present
Example:
<xsl:template match="">10]">
filthy rich
<xsl:apply-templates/>
</xsl:template>
xsl:for-each
I found that I was processing a lot of lists, and inserting commas and such between list items. Apply-templates doesn't work very well because you can't insert text around each match. So you need to use xsl:for-each. This takes one attribute, select, and works pretty much like apply-templates.
<!-- process all but the last element -->
<xsl:for-each select="*[position()<last()]">
<xsl:apply-templates select="."/> and
</xsl:for-each>
<!-- process the last element -->
<xsl:apply-templates select="*[position()=last()]">
xsl:value-of
When you are inside a template, you may want to print a value using the same XPath that you use to do matching and selections. Just use the xsl:value-of tag
xsl:value-of select="..."
name() prints out the name of the node inside the <> in your XML
@attributename prints an attribute
text() prints the text inside the braces
variables prefixed by $
xsl:variable
Sometimes you need variables. Here's how to create one, set its value, then display it:
<xsl:variable
name="hag"
select="name()"/>
<xsl:value-of select="$hag"/>
xsl:call-template
You can call a template from another template, so long as the template you are calling also has a unique name attribute. The name can't contain variables, etc (so this is not so useful for inheritance):
<xsl:call-template name="blah"/>
xsl:template and xsl:next-match and priorities
I needed to use inheritance right away. Unfortunately with XSL 1.0 and MSXML 3, you can't. But Saxon supports XSL 2.0, which contains next-match. What this does is that you can have multiple templates that match the same nodes. Set a priority attribute for each (lower numbers have a lower priority) and put xsl:next-match in the higher level priorities, and they will be evaluated in order. For example...
<xsl:template match="/*" priority="1001">
You have created an animal named <xsl:value-of select="name()">
<xsl:next-match/>
</xsl:template>
<xsl:template match="/dog" priority="1">
Woof woof
</xsl:template>
Will say 'You have created an animal named ...' for each node in the root. If you happen to have a node named 'dog', it will also say 'Woof woof' after the above text.
Using Saxon to Transform XML with XSL stylesheets
- Download saxon8.jar
- Make sure Java is installed (I think the JRE will work fine)
- Create an XML file
- Create an XSL file
- Run saxon like so from the directory that saxon8.jar is in:
java -jar saxon8.jar xmlxslt\test.xml xmlxslt\test.xsl
Labels
- Java (34)
- Oracle (27)
- javascript (24)
- NIX administration (19)
- Reporting (18)
- XML (17)
- Web Graphics (10)
- perl (10)
- CSS (9)
- Tomcat (8)
- Android (7)
- Matlab (7)
- XSL (7)
- HTML (6)
- SQL (6)
- XForms (6)
- browser quirks (6)
- Orbeon XForms (5)
- Solaris (5)
- AJAX (4)
- Mirth Project (4)
- PHP (4)
- Video (4)
- Arduino (3)
- Eclipse (3)
- JPA (3)
- JSP (3)
- JSTL (3)
- LAMPS (3)
- SSH (3)
- SVN (3)
- Hibernate (2)
- Netbeans (2)
- Networking (2)
- Python (2)
- Windows (2)
- Wordpress (2)
- XHTML (2)
- Alfresco (1)
- Architecture (1)
- ArduPilot (1)
- Arduino Yun (1)
- Arduplane (1)
- Audio Recording (1)
- Betaflight (1)
- CouchDB (1)
- DIY (1)
- Design (1)
- FPV (1)
- JSON (1)
- JUnit (1)
- Mobile Development (1)
- Printing (1)
- RC Airplane (1)
- REST (1)
- Scalability (1)
- Struts (1)
- Tools (1)
- Virtualization (1)
- Web services (1)
- camera (1)
- canon (1)
- gphoto2 (1)
- jQuery (1)
- ubuntu (1)
- unix (1)