Skip Headers
Oracle® XML DB Developer's Guide
10g Release 2 (10.2)

Part Number B14259-02
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Index
Index
Go to Master Index
Master Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

16 Generating XML Data from the Database

This chapter describes Oracle XML DB options for generating XML from the database. It explains the SQL/XML standard functions and Oracle Database-provided functions and packages for generating XML data from relational content.

This chapter contains these topics:

Oracle XML DB Options for Generating XML Data From Oracle Database

Oracle Database supports native XML generation. Oracle provides you with several options for generating or regenerating XML data when stored in:

The following discussion illustrates the Oracle XML DB options you can use to generate XML from Oracle Database.

Overview of Generating XML Using Standard SQL/XML Functions

You can generate XML data using any of the following standard SQL/XML functions supported by Oracle XML DB:

Overview of Generating XML Using Oracle Database SQL Functions

You can generate XML data using any of the following Oracle Database SQL functions:

Overview of Generating XML Using DBMS_XMLGEN

You can generate XML from SQL queries using PL/SQL package DBMS_XMLGEN.

Overview of Generating XML with XSQL Pages Publishing Framework

You can generate XML using XSQL Pages Publishing Framework. XSQL Pages Publishing Framework, also known as XSQL Servlet, is part of the XDK for Java.

Overview of Generating XML Using XML SQL Utility (XSU)

You can use XML SQL Utility (XSU) to perform the following tasks on data in XMLType tables and columns:

  • Transform data retrieved from object-relational database tables or views into XML.

  • Extract data from an XML document, and using a canonical mapping, insert the data into appropriate columns or attributes of a table or a view.

  • Extract data from an XML document and apply this data to updating or deleting values of the appropriate columns or attributes.

Overview of Generating XML Using DBURIType

You can use an instance of DBURIType to construct XML documents that contain database data and whose structure reflects the database structure.

Generating XML Using SQL Functions

This section describes Oracle XML DB SQL functions that you can use to generate XML data. Many of these functions belong to the SQL/XML standard, a SQL standard for XML:

The SQL/XML standard is ISO/IEC 9075–14:2005(E), Information technology – Database languages – SQL – Part 14: XML-Related Specifications (SQL/XML). As part of the SQL standard, it is aligned with SQL:2003. It is being developed under the auspices of these two standards bodies:

This standardization process is ongoing. Please refer to http://www.sqlx.org for the latest information about XMLQuery and XMLTable.

Other XML-generating SQL functions presented in this section are Oracle Database-specific:

All of the XML-generation SQL functions convert scalars and user-defined datatype instances to their canonical XML format. In this canonical mapping, user-defined datatype attributes are mapped to XML elements.

XMLELEMENT and XMLATTRIBUTES SQL Functions

You use SQL function XMLElement to construct XML instances from relational data. It takes as arguments an element name, an optional collection of attributes for the element, and zero or more additional arguments that make up the element content. It returns an XMLType instance.

Figure 16-1 XMLELEMENT Syntax

Description of Figure 16-1 follows
Description of "Figure 16-1 XMLELEMENT Syntax"

The first argument to function XMLElement (identifier, in Figure 16-1) is an identifier that names the root XML element to be created. This root-element identifier need not be a column name or a column reference, and it cannot be an expression to be evaluated. If the identifier is NULL, then no root element is generated.

The optional XML-attributes-clause argument of function XMLElement specifies the attributes of the root element to be generated. Figure 16-2 shows the syntax of this argument.

In addition to the optional XML-attributes-clause argument, function XMLElement accepts zero or more value_expr arguments that make up the content of the root element (child elements and text content). If an XML-attributes-clause argument is also present, these content arguments must follow the XML-attributes-clause argument. Each of the content-argument expressions is evaluated, and the result is converted to XML format. If a value argument evaluates to NULL, then no content is created for that argument.

The optional XML-attributes-clause argument uses function XMLAttributes to specify the attributes of the root element. Function XMLAttributes can be used only in a call to function XMLElement; it cannot be used on its own.

Figure 16-2 XMLAttributes Clause Syntax (XMLATTRIBUTES)

Description of Figure 16-2 follows
Description of "Figure 16-2 XMLAttributes Clause Syntax (XMLATTRIBUTES)"

Argument XML-attributes-clause itself contains one or more value_expr expressions as arguments to function XMLAttributes. These are evaluated to obtain the values for the attributes of the root element. (Do not confuse these value_expr arguments to function XMLAttributes with the value_expr arguments to function XMLElement, which specify the content of the root element.) The optional AS c_alias clause for each value_expr specifies that the attribute name is c_alias (a string literal).

If an attribute value expression evaluates to NULL, then no corresponding attribute is created. The datatype of an attribute value expression cannot be an object type or a collection.

Escaping Characters in Generated XML Data

As specified by the SQL/XML standard, characters in explicit identifiers are not escaped in any way – it is up to you to ensure that valid XML names are used. This applies to all SQL/XML functions; in particular, it applies to the root-element identifier of XMLElement (identifier, in Figure 16-1) and to attribute identifier aliases named with AS clauses of XMLAttributes (see Figure 16-2).

However, other XML data that is generated is escaped, to provide that only valid XML NameChar characters are generated. As part of generating a valid XML element or attribute name from a SQL identifier, each character that is disallowed in an XML name is replaced with an underscore character (_), followed by the hexadecimal Unicode representation of the original character, followed by a second underscore character. For example, the colon character (:) is escaped by replacing it with _003A_, where 003A is the hexadecimal Unicode representation.

Escaping applies to characters in the evaluated value_expr arguments to all SQL/XML functions, including XMLElement and XMLAttributes. It applies also to the characters of an attribute identifier that is defined implicitly from an XMLAttributes attribute value expression that is not followed by an AS clause: the escaped form of the SQL column name is used as the name of the attribute.

Formatting of XML Dates and Timestamps

The XML Schema standard specifies that dates and timestamps in XML data be in standard formats. XML generation functions in Oracle XML DB produce XML dates and timestamps according to this standard.

In releases prior to Oracle Database 10g Release 2, the database settings for date and timestamp formats were used for XML, instead of the XML Schema standard formats. You can reproduce this previous behavior by setting the database event 19119, level 0x8, as follows:

ALTER SESSION SET EVENTS '19119 TRACE NAME CONTEXT FOREVER, LEVEL 0x8';

If you otherwise need to produce a non-standard XML date or timestamp, use SQL function to_char – see Example 16-1.

See Also:

http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/datatypes.html#isoformats for the XML Schema specification of XML date and timestamp formats

XMLElement Examples

This section provides examples that use SQL function XMLElement.

Example 16-1 XMLELEMENT: Formatting a Date

This example shows how to produce an XML date with a format different from the XML Schema standard format.

-- With standard XML date format:
SELECT XMLElement("Date", hire_date)
  FROM hr.employees
  WHERE employee_id = 203;
 
XMLELEMENT("DATE",HIRE_DATE)
----------------------------
<Date>1994-06-07</Date>
 
1 row selected.
 
-- With an alternative date format:
SELECT XMLElement("Date", to_char(hire_date))
  FROM hr.employees
  WHERE employee_id = 203;
 
XMLELEMENT("DATE",TO_CHAR(HIRE_DATE))
-------------------------------------
<Date>07-JUN-94</Date>
 
1 row selected.

Example 16-2 XMLELEMENT: Generating an Element for Each Employee

This example produces an Emp element for each employee, with the employee name as its content:

SELECT e.employee_id, 
       XMLELEMENT ("Emp", e.first_name ||' '|| e.last_name) AS "RESULT"
   FROM hr.employees e
   WHERE employee_id > 200;

This query produces the following typical result:

EMPLOYEE_ID RESULT
----------- -----------------------------------
        201 <Emp>Michael Hartstein</Emp>
        202 <Emp>Pat Fay</Emp>
        203 <Emp>Susan Mavris</Emp>
        204 <Emp>Hermann Baer</Emp>
        205 <Emp>Shelley Higgins</Emp>
        206 <Emp>William Gietz</Emp>
 
6 rows selected.

SQL function XMLElement can also be nested, to produce XML data with a nested structure.

Example 16-3 XMLELEMENT: Generating Nested XML

To produce an Emp element for each employee, with elements that provide the employee name and hire date, do the following:

SELECT XMLElement("Emp", 
                   XMLElement("name", e.first_name ||' '|| e.last_name),
                   XMLElement("hiredate", e.hire_date)) AS "RESULT" 
FROM hr.employees e 
WHERE employee_id > 200 ;

This query produces the following typical XML result:

RESULT
-----------------------------------------------------------------------
<Emp><name>Michael Hartstein</name><hiredate>1996-02-17</hiredate></Emp>
<Emp><name>Pat Fay</name><hiredate>1997-08-17</hiredate></Emp>
<Emp><name>Susan Mavris</name><hiredate>1994-06-07</hiredate></Emp>
<Emp><name>Hermann Baer</name><hiredate>1994-06-07</hiredate></Emp>
<Emp><name>Shelley Higgins</name><hiredate>1994-06-07</hiredate></Emp>
<Emp><name>William Gietz</name><hiredate>1994-06-07</hiredate></Emp>
 
6 rows selected.

Example 16-4 XMLELEMENT: Generating Employee Elements with ID and Name Attributes

This example produces an Emp element for each employee, with an id and name attribute:

SELECT XMLElement("Emp", XMLAttributes(
                           e.employee_id as "ID",
                           e.first_name ||' ' || e.last_name AS "name"))
  AS "RESULT"
  FROM hr.employees e
  WHERE employee_id > 200;

This query produces the following typical XML result fragment:

RESULT
-----------------------------------------------
<Emp ID="201" name="Michael Hartstein"></Emp>
<Emp ID="202" name="Pat Fay"></Emp>
<Emp ID="203" name="Susan Mavris"></Emp>
<Emp ID="204" name="Hermann Baer"></Emp>
<Emp ID="205" name="Shelley Higgins"></Emp>
<Emp ID="206" name="William Gietz"></Emp>
 
6 rows selected.

As mentioned in "Escaping Characters in Generated XML Data", characters in the root-element name and the names of any attributes defined by AS clauses are not escaped. Characters in an identifier name are escaped only if the name is created from an evaluated expression (such as a column reference). The following query shows that the root-element name and the attribute name are not escaped. Invalid XML is produced because greater-than sign (>) and a comma (,) are not allowed in XML element and attribute names.

SELECT XMLElement("Emp->Special", 
                  XMLAttributes(e.last_name || ', ' || e.first_name
                                AS "Last,First"))
   AS "RESULT"
   FROM hr.employees e
   WHERE employee_id = 201;

This query produces the following result, which is not well-formed XML:

RESULT
--------------------------------------------------------------------
<Emp->Special Last,First="Hartstein, Michael"></Emp->Special>

1 row selected.

A full description of character escaping is included in the SQL/XML standard.

Example 16-5 XMLELEMENT: Using Namespaces to Create a Schema-Based XML Document

This example illustrates the use of namespaces to create an XML schema-based document. Assuming that an XML schema "http://www.oracle.com/Employee.xsd" exists and has no target namespace, then the following query creates an XMLType instance conforming to that schema:

SELECT XMLElement("Employee", 
                  XMLAttributes('http://www.w3.org/2001/XMLSchema' AS
                                  "xmlns:xsi",
                                'http://www.oracle.com/Employee.xsd' AS
                                  "xsi:nonamespaceSchemaLocation"),
                  XMLForest(employee_id, last_name, salary)) AS "RESULT"
   FROM hr.employees
   WHERE department_id = 10;

This creates the following XML document that conforms to XML schema Employee.xsd (the actual result is not pretty-printed).

RESULT
-----------------------------------------------------------------------------
<Employee xmlns:xsi="http://www.w3.org/2001/XMLSchema"
          xsi:nonamespaceSchemaLocation="http://www.oracle.com/Employee.xsd">
   <EMPLOYEE_ID>200</EMPLOYEE_ID>
   <LAST_NAME>Whalen</LAST_NAME>
   <SALARY>4400</SALARY>
</Employee>

1 row selected.

Example 16-6 XMLELEMENT: Generating an Element from a User-Defined Datatype Instance

Example 16-10 shows an XML document with employee information. You can generate a hierarchical XML document with the employee and department information as follows:

CREATE OR REPLACE TYPE emp_t AS OBJECT ("@EMPNO" NUMBER(4),
                                         ENAME VARCHAR2(10));
/
Type created.

CREATE OR REPLACE TYPE emplist_t AS TABLE OF emp_t;
/
Type created.

CREATE OR REPLACE TYPE dept_t AS OBJECT ("@DEPTNO" NUMBER(2),
                                         DNAME VARCHAR2(14),
                                         EMP_LIST emplist_t);
/
Type created.

SELECT XMLElement("Department",
                  dept_t(department_id,
                         department_name,
                         CAST(MULTISET(SELECT employee_id, last_name
                                         FROM hr.employees e
                                         WHERE e.department_id = d.department_id)
                              AS emplist_t)))
  AS deptxml
  FROM hr.departments d
  WHERE d.department_id = 10;

This produces an XML document which contains the Department element and the canonical mapping of type dept_t.

DEPTXML
-------------
<Department>
  <DEPT_T DEPTNO="10">
    <DNAME>ACCOUNTING</DNAME>
    <EMPLIST>
      <EMP_T EMPNO="7782">
        <ENAME>CLARK</ENAME>
      </EMP_T>
      <EMP_T EMPNO="7839">
        <ENAME>KING</ENAME>
      </EMP_T>
      <EMP_T EMPNO="7934">
        <ENAME>MILLER</ENAME>
      </EMP_T>
    </EMPLIST>
  </DEPT_T>
</Department>

1 row selected.

XMLFOREST SQL Function

SQL function XMLForest produces a forest of XML elements from its arguments, which are expressions to be evaluated, with optional aliases. Figure 16-3 describes the XMLForest syntax.

Figure 16-3 XMLFOREST Syntax

Description of Figure 16-3 follows
Description of "Figure 16-3 XMLFOREST Syntax"

Each of the value expressions (value_expr in Figure 16-3) is converted to XML format, and, optionally, identifier c_alias is used as the attribute identifier.

For an object type or collection, the AS clause is required. For other types, the AS clause is optional. For a given expression, if the AS clause is omitted, then characters in the evaluated value expression are escaped to form the name of the enclosing tag of the element. The escaping is as defined in "Escaping Characters in Generated XML Data". If the value expression evaluates to NULL, then no element is created for that expression.

Example 16-7 XMLFOREST: Generating Elements with Attribute and Child Elements

This example generates an Emp element for each employee, with a name attribute and elements with the employee hire date and department as the content.

SELECT XMLElement("Emp", 
                  XMLAttributes(e.first_name ||' '|| e.last_name AS "name"),
                  XMLForest(e.hire_date, e.department AS "department"))
AS "RESULT"
FROM employees e WHERE e.department_id = 20;

(The WHERE clause is used here to keep the example brief.) This query produces the following XML result:

RESULT
-------------------------------------
<Emp name="Michael Hartstein">
  <HIRE_DATE>1996-02-17</HIRE_DATE>
  <department>20</department>
</Emp>
<Emp name="Pat Fay">
  <HIRE_DATE>1997-08-17</HIRE_DATE>
  <department>20</department>
</Emp>

2 rows selected.

Example 16-8 XMLFOREST: Generating an Element from a User-Defined Datatype Instance

You can also use SQL function XMLForest to generate hierarchical XML from user-defined datatype instances.

SELECT XMLForest(
  dept_t(department_id,
         department_name,
         CAST (MULTISET (SELECT employee_id, last_name
                           FROM hr.employees e
                           WHERE e.department_id = d.department_id)
               AS emplist_t))
         AS "Department")
  AS deptxml
  FROM hr.departments d
  WHERE department_id=10;

This produces an XML document with element Department containing attribute DEPTNO and child element DNAME.

DEPTXML
---------------------------------
<Department DEPTNO="10">
  <DNAME>Administration</DNAME>
    <EMP_LIST>
      <EMP_T EMPNO="200">
        <ENAME>Whalen</ENAME>
      </EMP_T>
    </EMP_LIST>
</Department>

1 row selected.

You may want to compare this example with Example 16-6 and Example 16-27.

XMLSEQUENCE SQL Function

SQL function XMLSequence returns an XMLSequenceType value (a varray of XMLType instances). Because it returns a collection, this function can be used in the FROM clause of SQL queries. See Figure 16-4.

Figure 16-4 XMLSEQUENCE Syntax

Description of Figure 16-4 follows
Description of "Figure 16-4 XMLSEQUENCE Syntax"

Example 16-9 XMLSEQUENCE Returns Only Top-Level Element Nodes

Function XMLSequence returns only top-level element nodes. That is, it will not shred attributes or text nodes.

SELECT value(T).getstringval() Attribute_Value
  FROM table(XMLSequence(extract(XMLType('<A><B>V1</B><B>V2</B><B>V3</B></A>'),
                                 '/A/B'))) T;

ATTRIBUTE_VALUE
----------------------
<B>V1</B>
<B>V2</B>
<B>V3</B>

3 rows selected.

Function XMLSequence has two forms:

  • The first form takes as input an XMLType instance, and returns a varray of top-level nodes. This form can be used to shred XML fragments into multiple rows.

  • The second form takes as input a REFCURSOR instance and an optional instance of the XMLFormat object, and returns a varray of XMLType instances corresponding to each row of the cursor. This form can be used to construct XMLType instances from arbitrary SQL queries. This use of XMLFormat does not support XML schemas.

Function XMLSequence is essential for effective SQL queries involving XMLType instances.

Example 16-10 XMLSEQUENCE: Generating One XML Document from Another

Consider the following XMLType table containing an XML document with employee information:

CREATE TABLE emp_xml_tab OF XMLType;
 
Table created.

INSERT INTO emp_xml_tab VALUES( XMLType('<EMPLOYEES>
                                           <EMP>
                                             <EMPNO>112</EMPNO> 
                                             <EMPNAME>Joe</EMPNAME>
                                             <SALARY>50000</SALARY>
                                           </EMP>
                                           <EMP>
                                             <EMPNO>217</EMPNO>
                                             <EMPNAME>Jane</EMPNAME> 
                                             <SALARY>60000</SALARY>
                                           </EMP>
                                           <EMP> 
                                             <EMPNO>412</EMPNO> 
                                             <EMPNAME>Jack</EMPNAME>
                                             <SALARY>40000</SALARY>
                                           </EMP>
                                         </EMPLOYEES>'));

1 row created.

COMMIT;

To create a new XML document containing only employees who earn $50,000 or more, you can use the following query:

SELECT sys_XMLAgg(value(em), XMLFormat('EMPLOYEES'))
  FROM emp_xml_tab doc, table(XMLSequence(extract(value(doc), 
                                          '/EMPLOYEES/EMP'))) em
  WHERE extractValue(value(em), '/EMP/SALARY') >= 50000;

These are the steps involved in this query:

  1. Function extract returns a fragment of EMP elements.

  2. Function XMLSequence gathers a collection of these top-level elements into XMLType instances and returns that.

  3. Function table makes a table value from the collection. The table value is then used in the query FROM clause.

The query returns the following XML document:

SYS_XMLAGG(VALUE(EM),XMLFORMAT('EMPLOYEES'))
--------------------------------------------
<?xml version="1.0"?>
<EMPLOYEES>
  <EMP>
    <EMPNO>112</EMPNO>
    <EMPNAME>Joe</EMPNAME>
    <SALARY>50000</SALARY>
  </EMP>
  <EMP>
    <EMPNO>217</EMPNO>
    <EMPNAME>Jane</EMPNAME>
    <SALARY>60000</SALARY>
  </EMP>
</EMPLOYEES>
 
1 row selected.

Example 16-11 XMLSEQUENCE: Generate a Document for Each Row of a Cursor

In this example, SQL function XMLSequence is used to create an XML document for each row of a cursor expression, and it returns an XMLSequenceType value (a varray of XMLType instances).

SELECT value(em).getClobVal() AS "XMLTYPE"
  FROM table(XMLSequence(Cursor(SELECT * 
                                  FROM hr.employees
                                  WHERE employee_id = 104))) em;

This query returns the following XML:

XMLTYPE
--------------------------------------------------
 <ROW>
  <EMPLOYEE_ID>104</EMPLOYEE_ID>
  <FIRST_NAME>Bruce</FIRST_NAME>
  <LAST_NAME>Ernst</LAST_NAME>
  <EMAIL>BERNST</EMAIL>
  <PHONE_NUMBER>590.423.4568</PHONE_NUMBER>
  <HIRE_DATE>21-MAY-91</HIRE_DATE>
  <JOB_ID>IT_PROG</JOB_ID>
  <SALARY>6000</SALARY>
  <MANAGER_ID>103</MANAGER_ID>
  <DEPARTMENT_ID>60</DEPARTMENT_ID>
 </ROW>
 
1 row selected.

The tag used for each row can be changed using the XMLFormat object.

Example 16-12 XMLSEQUENCE: Unnesting Collections in XML Documents into SQL Rows

Because SQL function XMLSequence is a table function, it can be used to unnest the elements inside an XML document. For example, consider the following XMLType table dept_xml_tab containing XML documents:

CREATE TABLE dept_xml_tab OF XMLType;
 
Table created.
 
INSERT INTO dept_xml_tab
  VALUES(
    XMLType('<Department deptno="100">
               <DeptName>Sports</DeptName>
               <EmployeeList>
                 <Employee empno="200"><Ename>John</Ename><Salary>33333</Salary>
                 </Employee>
                 <Employee empno="300"><Ename>Jack</Ename><Salary>333444</Salary>
                 </Employee>
               </EmployeeList>
             </Department>'));
 
1 row created.
 
INSERT INTO dept_xml_tab
  VALUES (
    XMLType('<Department deptno="200">
               <DeptName>Sports</DeptName>
               <EmployeeList>
                 <Employee empno="400"><Ename>Marlin</Ename><Salary>20000</Salary>
                 </Employee>
               </EmployeeList>
             </Department>'));
 
1 row created.
 
COMMIT;

You can use SQL function XMLSequence to unnest the Employee list items as top-level SQL rows:

SELECT extractValue(OBJECT_VALUE, '/Department/@deptno') AS deptno,
       extractValue(value(em), '/Employee/@empno') AS empno,
       extractValue(value(em), '/Employee/Ename') AS ename
  FROM dept_xml_tab, 
       table(XMLSequence(extract(OBJECT_VALUE,
                                 '/Department/EmployeeList/Employee'))) em;

This returns the following:

DEPTNO       EMPNO          ENAME
---------------------------------
100           200           John
100           300           Jack
200           400           Marlin

3 rows selected

For each row in table dept_xml_tab, function table is applied. Here, function extract creates a new XMLType instance that contains a fragment of all employee elements. This is fed to SQL function XMLSequence, which creates a collection of all employees.

Function TABLE then explodes the collection elements into multiple rows which are correlated with the parent table dept_xml_tab. Thus you get a list of all the parent dept_xml_tab rows with the associated employees.

Function extractValue extracts out the scalar values for the department number, employee number, and name.

XMLCONCAT SQL Function

SQL function XMLConcat concatenates all of its arguments to create an XML fragment. Figure 16-5 shows the XMLConcat syntax. Function XMLConcat has two forms:

  • The first form takes an XMLSequenceType value (a varray of XMLType instances) and returns a single XMLType instance that is the concatenation of all of the elements of the varray. This form is useful to collapse lists of XMLTypes into a single instance.

  • The second form takes an arbitrary number of XMLType instances and concatenates them together. If one of the values is NULL, then it is ignored in the result. If all the values are NULL, then the result is NULL. This form is used to concatenate arbitrary number of XMLType instances in the same row. Function XMLAgg can be used to concatenate XMLType instances across rows.

Figure 16-5 XMLCONCAT Syntax

Description of Figure 16-5 follows
Description of "Figure 16-5 XMLCONCAT Syntax"

Example 16-13 XMLCONCAT: Concatenating XMLType Instances from a Sequence

This example uses function XMLConcat to return a concatenation of XMLType instances from an XMLSequenceType value (a varray of XMLType instances).

SELECT XMLConcat(XMLSequenceType(
                   XMLType('<PartNo>1236</PartNo>'), 
                   XMLType('<PartName>Widget</PartName>'),
                   XMLType('<PartPrice>29.99</PartPrice>'))).getClobVal()
  AS "RESULT"
  FROM DUAL;

This query returns a single XML fragment (the actual output is not pretty-printed):

RESULT
---------------
<PartNo>1236</PartNo>
<PartName>Widget</PartName>
<PartPrice>29.99</PartPrice>

1 row selected.

Example 16-14 XMLCONCAT: Concatenating XML Elements

The following example creates an XML element for the first and the last names and then concatenates the result:

SELECT XMLConcat(XMLElement("first", e.first_name), 
                 XMLElement("last", e.last_name))
  AS "RESULT"
  FROM employees e;

This query produces the following XML fragment:

RESULT
--------------------------------------------
<first>Den</first><last>Raphaely</last>
<first>Alexander</first><last>Khoo</last>
<first>Shelli</first><last>Baida</last>
<first>Sigal</first><last>Tobias</last>
<first>Guy</first><last>Himuro</last>
<first>Karen</first><last>Colmenares</last>
 
6 rows selected.

XMLAGG SQL Function

SQL function XMLAgg is an aggregate function that produces a forest of XML elements from a collection of XML elements.

Figure 16-6 XMLAGG Syntax

Description of Figure 16-6 follows
Description of "Figure 16-6 XMLAGG Syntax"

Figure 16-6 describes the XMLAgg() syntax, where the order_by_clause is the following:

ORDER BY [list of: expr [ASC|DESC] [NULLS {FIRST|LAST}]]

Numeric literals are not interpreted as column positions. For example, ORDER BY 1 does not mean order by the first column. Instead, numeric literals are interpreted as any other literals.

As with SQL function XMLConcat, any arguments that are NULL are dropped from the result. Function XMLAgg is similar to function sys_XMLAgg, except that it returns a forest of nodes and does not take the XMLFormat parameter. Function XMLAgg can be used to concatenate XMLType instances across multiple rows. It also allows an optional ORDER BY clause, to order the XML values being aggregated.

Function XMLAgg produces one aggregated XML result for each group. If there is no group by specified in the query, then it returns a single aggregated XML result for all the rows of the query.

Example 16-15 XMLAGG: Generating Department Elements with a List of Employee Elements

This example produces a Department element containing Employee elements with employee job ID and last name as the contents of the elements. It also orders the employee XML elements in the department by their last name. (The actual result is not pretty-printed.)

SELECT XMLElement("Department", XMLAgg(XMLElement("Employee",
                                                  e.job_id||' '||e.last_name)
                                       ORDER BY e.last_name))
  AS "Dept_list"     
  FROM hr.employees e
  WHERE e.department_id = 30 OR e.department_id = 40;

Dept_list
------------------
<Department>
  <Employee>PU_CLERK Baida</Employee>
  <Employee>PU_CLERK Colmenares</Employee>
  <Employee>PU_CLERK Himuro</Employee>
  <Employee>PU_CLERK Khoo</Employee>
  <Employee>HR_REP Mavris</Employee>
  <Employee>PU_MAN Raphaely</Employee>
  <Employee>PU_CLERK Tobias</Employee>
</Department>

1 row selected.

The result is a single row, because XMLAgg aggregates the rows. You can use the GROUP BY clause to group the returned set of rows into multiple groups. (The actual result of the following query is not pretty-printed.)

SELECT XMLElement("Department", XMLAttributes(department_id AS "deptno"), 
                  XMLAgg(XMLElement("Employee", e.job_id||' '||e.last_name)))
   AS "Dept_list"
   FROM hr.employees e
   GROUP BY e.department_id;

Dept_list
------------------
<Department deptno="30">
  <Employee>PU_MAN Raphaely</Employee>
  <Employee>PU_CLERK Khoo</Employee>
  <Employee>PU_CLERK Baida</Employee>
  <Employee>PU_CLERK Himuro</Employee>
  <Employee>PU_CLERK Colmenares</Employee>
  <Employee>PU_CLERK Tobias</Employee>
</Department>

<Department deptno="40">
  <Employee>HR_REP Mavris</Employee>
</Department>

2 rows selected.

You can order the employees within each department by using the ORDER BY clause inside the XMLAgg expression.

Note:

Within the order_by_clause, Oracle Database does not interpret number literals as column positions, as it does in other uses of this clause, but simply as number literals.

Example 16-16 XMLAGG: Generating Nested Elements

Function XMLAgg can be used to reflect the hierarchical nature of some relationships that exist in tables. This example generates a department element for department 30. Within this element is a child element for each employee of the department. Within each employee element is a dependent element for each dependent of that employee.

First, this query shows the employees of department 30.

SELECT last_name, employee_id FROM employees WHERE department_id = 30;
 
LAST_NAME                 EMPLOYEE_ID
------------------------- -----------
Raphaely                          114
Khoo                              115
Baida                             116
Tobias                            117
Himuro                            118
Colmenares                        119
 
6 rows selected.
 

A dependents table is created, to hold the dependents of each employee.

CREATE TABLE hr.dependents (id NUMBER(4) PRIMARY KEY,
                            employee_id NUMBER(4),
                            name VARCHAR2(10));
Table created.
INSERT INTO dependents VALUES (1, 114, 'MARK');
1 row created.
INSERT INTO dependents VALUES (2, 114, 'JACK');
1 row created.
INSERT INTO dependents VALUES (3, 115, 'JANE');
1 row created.
INSERT INTO dependents VALUES (4, 116, 'HELEN');
1 row created.
INSERT INTO dependents VALUES (5, 116, 'FRANK');
1 row created.
COMMIT;
Commit complete.
 

This query generates the XML data for department that contains the information on dependents (the actual output is not pretty-printed):

SELECT
  XMLElement(
    "Department",
    XMLAttributes(d.department_name AS "name"),
    (SELECT
       XMLAgg(XMLElement("emp",
                         XMLAttributes(e.last_name AS name),
                         (SELECT XMLAgg(XMLElement("dependent",
                                        XMLAttributes(de.name AS "name")))
                            FROM dependents de
                            WHERE de.employee_id = e.employee_id)))
       FROM employees e
       WHERE e.department_id = d.department_id)) AS "dept_list"
  FROM departments d
  WHERE department_id = 30;
 
dept_list
--------------------------------------------------------------------------------
<Department name="Purchasing">
  <emp NAME="Raphaely">
    <dependent name="MARK"></dependent>
    <dependent name="JACK"></dependent>
  </emp><emp NAME="Khoo">
    <dependent name="JANE"></dependent>
  </emp>
  <emp NAME="Baida">
    <dependent name="HELEN"></dependent>
    <dependent name="FRANK"></dependent>
  </emp><emp NAME="Tobias"></emp>
  <emp NAME="Himuro"></emp>
  <emp NAME="Colmenares"></emp>
</Department>
 
1 row selected.

XMLPI SQL Function

You use SQL function XMLPI to generate XML processing instructions (PIs). Figure 16-7 shows the syntax:

Argument value_expr is evaluated, and the string result is appended to the optional identifier (identifier), separated by a space. This concatenation is then enclosed betweeen "<?" and "?>" to create the processing instruction. That is, if string-result is the result of evaluating value_expr, then the generated processing instruction is <?identifier string-result?>. If string-result is the empty string, '', then the function returns <?identifier?>.

As an alternative to using keyword NAME followed by a literal string identifier, you can use keyword EVALNAME followed by an expression that evaluates to a string to be used as the identifier.

An error is raised if the constructed XML is not a legal XML processing instruction. In particular:

  • identifier must not be the word "xml" (uppercase, lowercase, or mixed case).

  • string-result must not contain the character sequence "?>".

Function XMLPI returns an instance of XMLType. If string-result is NULL, then it returns NULL.

Example 16-17 Using XMLPI

SELECT XMLPI(NAME "OrderAnalysisComp", 'imported, reconfigured, disassembled')
  AS pi FROM DUAL; 

This results in the following output :

PI
----------------------------------------------------------
<?OrderAnalysisComp imported, reconfigured, disassembled?>

1 row selected.

XMLCOMMENT SQL Function

You use SQL function XMLComment to generate XML comments. Figure 16-8 shows the syntax:

Figure 16-8 XMLComment Syntax

Description of Figure 16-8 follows
Description of "Figure 16-8 XMLComment Syntax"

Argument value_expr is evaluated to a string, and the result is used as the body of the generated XML comment; that is, the result is <!--string-result-->, where string-result is the string result of evaluating value_expr. If string-result is the empty string, then the comment is empty: <!---->.

An error is raised if the constructed XML is not a legal XML comment. In particular, string-result must not contain two consecutive hyphens (-): "--".

Function XMLComment returns an instance of XMLType. If string-result is NULL, then the function returns NULL.

Example 16-18 Using XMLCOMMENT

SELECT XMLComment('This is a comment') AS cmnt FROM DUAL; 

This query results in the following output:

CMNT
--------------------------
<!--This is a comment-->

XMLROOT SQL Function

You use SQL function XMLRoot to add a VERSION property, and optionally a STANDALONE property, to the root information item of an XML value. Typically, this is done to ensure data-model compliance. Figure 16-9 shows the syntax of XMLRoot:

Figure 16-9 XMLRoot Syntax

Description of Figure 16-9 follows
Description of "Figure 16-9 XMLRoot Syntax"

First argument xml-expression is evaluated, and the indicated properties (VERSION, STANDALONE) and their values are added to a new prolog for the resulting XMLType instance. If the evaluated xml-expression already contains a prolog, then an error is raised.

Second argument string-valued-expression (which follows keyword VERSION) is evaluated, and the resulting string is used as the value of the prolog version property. The value of the prolog standalone property (lowercase) is taken from the optional third argument STANDALONE YES or NO value. If NOVALUE is used for VERSION, then "version=1.0" is used in the resulting prolog. If NOVALUE is used for STANDALONE, then the standalone property is omitted from the resulting prolog.

Function XMLRoot returns an instance of XMLType. If first argument xml-expression evaluates to NULL, then the function returns NULL.

Example 16-19 Using XMLRoot

SELECT XMLRoot(XMLType('<poid>143598</poid>'), VERSION '1.0', STANDALONE YES)
  AS xmlroot FROM DUAL; 

This results in the following output :

XMLROOT
--------------------------------------
<?xml version="1.0" standalone="yes"?>
<poid>143598</poid>

1 row selected.

XMLSERIALIZE SQL Function

You use SQL function XMLSerialize to obtain a string or a LOB representation of XML data. Figure 16-10 shows the syntax:

Figure 16-10 XMLSerialize Syntax

Description of Figure 16-10 follows
Description of "Figure 16-10 XMLSerialize Syntax"

Argument value_expr is evaluated, and the resulting XMLType instance is serialized to produce the content of the created string or LOB. If presentFoot 1 , the specified datatype must be one of the following (the default datatype is CLOB):

  • VARCHAR2

  • VARCHAR

  • CLOB

If you specify DOCUMENT, then the result of evaluating value_expr must be a well-formed document; in particular, it must have a single root. If the result is not a well-formed document, then an error is raised. If you specify CONTENT, however, then the result of value_expr is not checked for well-formedness.

If the underlying CLOB value or string has encoding information, then an appropriate encoding="..." declaration is added to the prolog.

If value_expr evaluates to NULL or to the empty string (''), then function XMLSerialize returns NULL.

Example 16-20 Using XMLSERIALIZE

SELECT XMLSerialize(DOCUMENT XMLType('<poid>143598</poid>') AS CLOB)
  AS xmlserialize_doc FROM DUAL; 

This results in the following output:

XMLSERIALIZE_DOC
-------------------
<poid>143598</poid>

XMLPARSE SQL Function

You use SQL function XMLParse to parse a string containing XML data and generate a corresponding value of XMLType. Figure 16-11 shows the syntax:

Figure 16-11 XMLParse Syntax

Description of Figure 16-11 follows
Description of "Figure 16-11 XMLParse Syntax"

Argument value_expr is evaluated to produce the string that is parsed. If you specify DOCUMENT, then value_expr must correspond to a singly rooted, well-formed XML document. If you specify CONTENT, then value_expr need only correspond to a well-formed XML fragment; that is, it need not be singly rooted.

Keyword WELLFORMED is an Oracle XML DB extension to the SQL/XML standard. When you specify WELLFORMED, you are informing the parser that argument value_expr is well-formed, so Oracle XML DB does not check to ensure that it is in fact well-formed.

Function XMLParse returns an instance of XMLType. If value_expr evaluates to NULL, then the function returns NULL.

Example 16-21 Using XMLPARSE

SELECT XMLParse(CONTENT 
                '124 <purchaseOrder poNo="12435">
                       <customerName> Acme Enterprises</customerName>
                       <itemNo>32987457</itemNo>
                     </purchaseOrder>'
                WELLFORMED)
  AS po FROM DUAL d;

This results in the following output :

PO
-----------------------------------------------
124 <purchaseOrder poNo="12435">
<customerName>Acme Enterprises</customerName>
<itemNo>32987457</itemNo>
</purchaseOrder>

See Also:

http://www.w3.org/TR/REC-xml/, Extensible Markup Language (XML) 1.0, for the definition of well-formed XML documents and fragments

XMLCOLATTVAL SQL Function

SQL function XMLColAttVal generates a forest of XML column elements containing the values of the arguments passed in. This function is an Oracle Database extension to the SQL/XML ANSI-ISO standard functions. Figure 16-12 shows the XMLColAttVal syntax.

Figure 16-12 XMLCOLATTVAL Syntax

Description of Figure 16-12 follows
Description of "Figure 16-12 XMLCOLATTVAL Syntax"

The arguments are used as the values of the name attribute of the column element. The c_alias values are used as the attribute identifiers.

As an alternative to using keyword AS followed by a literal string c_alias, you can use AS EVALNAME followed by an expression that evaluates to a string to be used as the attribute identifier.

Because argument values value_expr are used only as attribute values, they need not be escaped in any way. This is in contrast to function XMLForest. It means that you can use XMLColAttVal to transport SQL columns and values without escaping.

Example 16-22 XMLCOLATTVAL: Generating Elements with Attribute and Child Elements

This example generates an Emp element for each employee, with a name attribute and elements with the employee hire date and department as the content.

SELECT XMLElement("Emp", 
                  XMLAttributes(e.first_name ||' '||e.last_name AS "fullname" ),
                  XMLColAttVal(e.hire_date, e.department_id AS "department"))
  AS "RESULT" 
  FROM hr.employees e
  WHERE e.department_id = 30;

This query produces the following XML result (the actual result is not pretty-printed):

RESULT
-----------------------------------------------------------
<Emp fullname="Den Raphaely">
  <column name = "HIRE_DATE">1994-12-07</column>
  <column name = "department">30</column>
</Emp>
<Emp fullname="Alexander Khoo">
  <column name = "HIRE_DATE">1995-05-18</column>
  <column name = "department">30</column>
</Emp>
<Emp fullname="Shelli Baida">
  <column name = "HIRE_DATE">1997-12-24</column>
  <column name = "department">30</column>
</Emp>
<Emp fullname="Sigal Tobias">
  <column name = "HIRE_DATE">1997-07-24</column>
  <column name = "department">30</column>
</Emp>
<Emp fullname="Guy Himuro">
  <column name = "HIRE_DATE">1998-11-15</column>
  <column name = "department">30</column>
</Emp>
<Emp fullname="Karen Colmenares">
  <column name = "HIRE_DATE">1999-08-10</column>
  <column name = "department">30</column>
</Emp>
 
6 rows selected.

XMLCDATA SQL Function

You use SQL function XMLCDATA to generate an XML CDATA section. Figure 16-13 shows the syntax:

Figure 16-13 XMLCDATA Syntax

Description of Figure 16-13 follows
Description of "Figure 16-13 XMLCDATA Syntax"

Argument value_expr is evaluated to a string, and the result is used as the body of the generated XML CDATA section, <![CDATA[string-result]]>, where string-result is the result of evaluating value_expr. If string-result is the empty string, then the CDATA section is empty: <![CDATA[]]>.

An error is raised if the constructed XML is not a legal XML CDATA section. In particular, string-result must not contain two consecutive right brackets (]): "]]".

Function XMLCDATA returns an instance of XMLType. If string-result is NULL, then the function returns NULL.

Example 16-23 Using XMLCDATA

SELECT XMLElement("PurchaseOrder",
                  XMLElement("Address",
                             XMLCDATA('100 Pennsylvania Ave.'),
                             XMLElement("City", 'Washington, D.C.')))
  AS RESULT FROM DUAL;
                            

This results in the following output (the actual output is not pretty-printed):

RESULT
--------------------------
<PurchaseOrder>
  <Address>
    <![CDATA[100 Pennsylvania Ave.]]>
    <City>Washington, D.C.</City>
  </Address>
</PurchaseOrder>

Generating XML Using DBMS_XMLGEN

PL/SQL package DBMS_XMLGEN creates XML documents from SQL query results. It retrieves an XML document as a CLOB or XMLType value.

It provides a fetch interface, whereby you can specify the maximum number of rows to retrieve and the number of rows to skip. For example, the first fetch could retrieve a maximum of ten rows, skipping the first four. This is especially useful for pagination requirements in Web applications.

Package DBMS_XMLGEN also provides options for changing tag names for ROW, ROWSET, and so on. The parameters of the package can restrict the number of rows retrieved and the enclosing tag names.

See Also:

"Generating XML with XSU's OracleXMLQuery", in Oracle XML Developer's Kit Programmer's Guide (compare the functionality of OracleXMLQuery with DBMS_XMLGEN)

Using DBMS_XMLGEN

Figure 16-14 illustrates how to use package DBMS_XMLGEN. The steps are as follows:

  1. Get the context from the package by supplying a SQL query and calling the newContext() call.

  2. Pass the context to all procedures or functions in the package to set the various options. For example, to set the ROW element name, use setRowTag(ctx), where ctx is the context got from the previous newContext() call.

  3. Get the XML result, using getXML() or getXMLType(). By setting the maximum number of rows to be retrieved for each fetch using setMaxRows(), you can call either of these functions repeatedly, retrieving up to the maximum number of rows for each call. These functions return XML data (as a CLOB value and as an instance of XMLType, respectively), unless there are no rows retrieved; in that case, these functions return NULL. To determine how many rows were retrieved, use function getNumRowsProcessed().

  4. You can reset the query to start again and repeat step 3.

  5. Close the closeContext() to free up any resource allocated inside.

Figure 16-14 Using DBMS_XMLGEN

Description of Figure 16-14 follows
Description of "Figure 16-14 Using DBMS_XMLGEN"

In conjunction with a SQL query, method DBMS_XMLGEN.getXML() typically returns a result like the following as a CLOB value:

<?xml version="1.0"?>
<ROWSET>
 <ROW>
  <EMPLOYEE_ID>100</EMPLOYEE_ID>
  <FIRST_NAME>Steven</FIRST_NAME>
  <LAST_NAME>King</LAST_NAME>
  <EMAIL>SKING</EMAIL>
  <PHONE_NUMBER>515.123.4567</PHONE_NUMBER>
  <HIRE_DATE>17-JUN-87</HIRE_DATE>
  <JOB_ID>AD_PRES</JOB_ID>
  <SALARY>24000</SALARY>
  <DEPARTMENT_ID>90</DEPARTMENT_ID>
 </ROW>
 <ROW>
  <EMPLOYEE_ID>101</EMPLOYEE_ID>
  <FIRST_NAME>Neena</FIRST_NAME>
  <LAST_NAME>Kochhar</LAST_NAME>
  <EMAIL>NKOCHHAR</EMAIL>
  <PHONE_NUMBER>515.123.4568</PHONE_NUMBER>
  <HIRE_DATE>21-SEP-89</HIRE_DATE>
  <JOB_ID>AD_VP</JOB_ID>
  <SALARY>17000</SALARY>
  <MANAGER_ID>100</MANAGER_ID>
  <DEPARTMENT_ID>90</DEPARTMENT_ID>
 </ROW>
</ROWSET>

The default mapping between relational data and XML data is as follows:

  • Each row returned by the SQL query maps to an XML element with the default element name ROW.

  • Each column returned by the SQL query maps to a child element of the ROW element.

  • The entire result is wrapped in a ROWSET element.

  • Binary data is transformed to its hexadecimal representation.

Element names ROW and ROWSET can be replaced with names you choose, using DBMS_XMLGEN procedures setRowTagName() and setRowSetTagName(), respectively.

The CLOB value returned by getXML() has the same encoding as the database character set. If the database character set is SHIFTJIS, then the XML document returned is also SHIFTJIS.

Functions and Procedures of Package DBMS_XMLGEN

Table 16-1 describes the functions and procedures of package DBMS_XMLGEN.

Table 16-1 DBMS_XMLGEN Functions and Procedures

Function or Procedure Description

DBMS_XMLGEN type definitions

SUBTYPE ctxHandle IS NUMBER

The context handle used by all functions.

Document Type Definition (DTD) or schema specifications:

NONE CONSTANT NUMBER:= 0; -- supported for this release.

DTD CONSTANT NUMBER:= 1;

SCHEMA CONSTANT NUMBER:= 2;

Can be used in function getXML to specify whether to generate a DTD or XML schema or neither (NONE). Only the NONE specification is supported in this release.

FUNCTION PROTOTYPES

newContext()

Given a query string, generate a new context handle to be used in subsequent functions.

FUNCTION

newContext(
  queryString IN VARCHAR2)

Returns a new context

Parameter: queryString (IN)- the query string, the result of which must be converted to XML

Returns: Context handle. Call this function first to obtain a handle that you can use in the getXML() and other functions to get the XML back from the result.

FUNCTION

newContext(
  queryString IN SYS_REFCURSOR)
  RETURN ctxHandle;

Creates a new context handle from a PL/SQL cursor variable. The context handle can be used for the rest of the functions.

FUNCTION

newContextFromHierarchy(
  queryString IN VARCHAR2)
  RETURN ctxHandle;

Introduced in Oracle Database 10g Release 1 (10.0.1).

Parameter: queryString (IN) - the query string, the result of which must be converted to XML. The query is a hierarchical query typically formed using a CONNECT BY clause, and the result must have the same property as the result set generated by a CONNECT BY query. The result set must have only two columns, the level number and an XML value. The level number is used to determine the hierarchical position of the XML value within the result XML document.

Returns: Context handle. Call this function first to obtain a handle that you can use in the getXML() and other functions to get a hierarchical XML with recursive elements back from the result.

setRowTag()

Sets the name of the element separating all the rows. The default name is ROW.

PROCEDURE

setRowTag(ctx IN ctxHandle,
          rowTag IN VARCHAR2);

Parameters:

ctx(IN) - the context handle obtained from the newContext call.

rowTag(IN) - the name of the ROW element. A NULL value for rowTag indicates that you do not want the ROW element to be present.

Call this procedure to set the name of the ROW element, if you do not want the default ROW name to show up. You can also set rowTag to NULL to suppress the ROW element itself.

However, since getXML returns complete XML documents, not XML fragments, there must be a (single) root element. Therefore, an error is raised if both the rowTag value and the rowSetTag value (see setRowSetTag, next) are NULL and there is more than one column or row in the output.

setRowSetTag()

Sets the name of the document root element. The default name is ROWSET

PROCEDURE

setRowSetTag(ctx IN ctxHandle, 
             rowSetTag IN VARCHAR2);

Parameters:

ctx(IN) – the context handle obtained from the newContext call.

rowSetTag(IN) – the name of the document root element to be used in the output. A NULL value for rowSetTag indicates that you do not want the ROWSET element to be present.

Call this procedure to set the name of the document root element, if you do not want the default name ROWSET to be used. You can set rowSetTag to NULL to suppress printing of the document root element.

However, since functiongetXML returns complete XML documents, not XML fragments, there must be a (single) root element. Therefore, an error is raised if both the rowTag value and the rowSetTag value (see setRowTag, previous) are NULL and there is more than one column or row in the output, or if the rowSetTag value is NULL and there is more than one row in the output.

getXML()

Gets the XML document by fetching the maximum number of rows specified. It appends the XML document to the CLOB passed in.

PROCEDURE

getXML(ctx IN ctxHandle, 
       clobval IN OUT NCOPY clob, 
       dtdOrSchema IN number:= NONE);

Parameters:

ctx(IN) - The context handle obtained from the newContext() call,

clobval(IN/OUT) - the CLOB to which the XML document is to be appended,

dtdOrSchema(IN) - whether you should generate the DTD or Schema. This parameter is NOT supported.

Use this version of the getXML function, to avoid any extra CLOB copies and if you want to reuse the same CLOB for subsequent calls. This getXML() call is more efficient than the next flavor, though this involves that you create the LOB locator. When generating the XML, the number of rows indicated by the setSkipRows call are skipped, then the maximum number of rows as specified by the setMaxRows call (or the entire result if not specified) is fetched and converted to XML. Use the getNumRowsProcessed function to check if any rows were retrieved or not.

getXML()

Generates the XML document and returns it as a CLOB.

FUNCTION

getXML(ctx IN ctxHandle, 
       dtdOrSchema IN number:= NONE) 
  RETURN clob;

Parameters:

ctx(IN) - The context handle obtained from the newContext() call,

dtdOrSchema(IN) - whether we should generate the DTD or Schema. This parameter is not supported.

Returns: A temporary CLOB containing the document. Free the temporary CLOB obtained from this function using the DBMS_LOB.freeTemporary call.

FUNCTION

getXMLType(
  ctx IN ctxHandle, 
  dtdOrSchema IN number:= NONE)
  RETURN XMLType;

Parameters:

ctx(IN) - The context handle obtained from the newContext() call,

dtdOrSchema(IN) - whether we should generate the DTD or Schema. This parameter is not supported.

Returns: An XMLType instance containing the document.

FUNCTION

getXML(
  sqlQuery IN VARCHAR2, 
  dtdOrSchema IN NUMBER := NONE)
  RETURN CLOB;

Converts the query results from the passed in SQL query string to XML format, and returns the XML as a CLOB.

FUNCTION

getXMLType(
  sqlQuery IN VARCHAR2, 
  dtdOrSchema IN NUMBER := NONE)   
  RETURN XMLType;

Converts the query results from the passed in SQL query string to XML format, and returns the XML as a CLOB.

getNumRowsProcessed()

Gets the number of SQL rows processed when generating XML data using functiongetXML . This count does not include the number of rows skipped before generating XML data.

FUNCTION

getNumRowsProcessed(ctx IN ctxHandle) 
  RETURN number;

Parameter: queryString(IN)- the query string, the result of which needs to be converted to XML

Returns: The number of SQL rows that were processed in the last call to getXML.

You can call this to find out if the end of the result set has been reached. This does not include the number of rows skipped before generating XML data. Use this function to determine the terminating condition if you are calling getXML in a loop. Note that getXML would always generate an XML document even if there are no rows present.

setMaxRows()

Sets the maximum number of rows to fetch from the SQL query result for every invocation of the getXML call. It is an error to call this function on a context handle created by newContextFromHierary() function

PROCEDURE

setMaxRows(ctx IN ctxHandle, 
           maxRows IN NUMBER);

Parameters:

ctx(IN) - the context handle corresponding to the query executed,

maxRows(IN) - the maximum number of rows to get for each call to getXML.

The maxRows parameter can be used when generating paginated results using this utility. For instance when generating a page of XML or HTML data, you can restrict the number of rows converted to XML and then in subsequent calls, you can get the next set of rows and so on. This also can provide for faster response times. It is an error to call this procedure on a context handle created by newContextFromHierary() function

setSkipRows()

Skips a given number of rows before generating the XML output for every call to the getXML() routine. It is an error to call this function on a context handle created by function newContextFormHierarchy().

PROCEDURE

setSkipRows(ctx IN ctxHandle, 
            skipRows IN NUMBER);

Parameters:

ctx(IN) - the context handle corresponding to the query executed,

skipRows(IN) - the number of rows to skip for each call to getXML.

The skipRows parameter can be used when generating paginated results for stateless web pages using this utility. For instance when generating the first page of XML or HTML data, you can set skipRows to zero. For the next set, you can set the skipRows to the number of rows that you got in the first case. It is an error to call this function on a context handle created by newContextFromHierarchy() function.

setConvertSpecialChars()

Sets whether special characters in the XML data need to be converted into their escaped XML equivalent or not. For example, the < sign is converted to &lt;. The default is to perform escape conversions.

PROCEDURE

setConvertSpecialChars(
  ctx IN ctxHandle, 
  conv IN BOOLEAN);

Parameters:

ctx(IN) - the context handle to use,

conv(IN) - true indicates that conversion is needed.

You can use this function to speed up the XML processing whenever you are sure that the input data cannot contain any special characters such as <, >, ", ' , and so on, which must be preceded by an escape character. Note that it is expensive to actually scan the character data to replace the special characters, particularly if it involves a lot of data. So in cases when the data is XML-safe, then this function can be called to improve performance.

useItemTagsForColl()

Sets the name of the collection elements. The default name for collection elements it the type name itself. You can override that to use the name of the column with the _ITEM tag appended to it using this function.

PROCEDURE

useItemTagsForColl(ctx IN ctxHandle);

Parameter: ctx(IN) - the context handle.

If you have a collection of NUMBER, say, the default tag name for the collection elements is NUMBER. You can override this action and generate the collection column name with the _ITEM tag appended to it, by calling this procedure.

restartQuery()

Restarts the query and generate the XML from the first row again.

PROCEDURE

restartQuery(ctx IN ctxHandle);

Parameter: ctx(IN) - the context handle corresponding to the current query. You can call this to start executing the query again, without having to create a new context.

closeContext()

Closes a given context and releases all resources associated with that context, including the SQL cursor and bind and define buffers, and so on.

PROCEDURE

closeContext(ctx IN ctxHandle);

Parameter: ctx(IN) - the context handle to close. Closes all resources associated with this handle. After this you cannot use the handle for any other DBMS_XMLGEN function call.

Conversion Functions

FUNCTION

convert(
  xmlData IN varchar2, 
  flag IN NUMBER := ENTITY_ENCODE)    
  RETURN VARCHAR2;

Encodes or decodes the XML data string argument.

  • Encoding refers to replacing entity references such as '<' to their escaped equivalent, such as '&lt;'.

  • Decoding refers to the reverse conversion.

FUNCTION

convert(
  xmlData IN CLOB, 
  flag IN NUMBER := ENTITY_ENCODE) 
  RETURN CLOB;

Encodes or decodes the passed in XML CLOB data.

  • Encoding refers to replacing entity references such as '<' to their escaped equivalent, such as '&lt;'.

  • Decoding refers to the reverse conversion.

NULL Handling

PROCEDURE

setNullHandling(ctx IN ctxHandle, 
                flag IN NUMBER);

Introduced in Oracle9i Release 2 (9.2.0.2).

The setNullHandling flag values are:

  • DROP_NULLS CONSTANT NUMBER := 0; This is the default setting and leaves out the tag for NULL elements.

  • NULL_ATTR CONSTANT NUMBER := 1; This sets xsi:nil="true".

  • EMPTY_TAG CONSTANT NUMBER := 2; This sets, for example, <foo/>.

PROCEDURE

useNullAttributeIndicator(
  ctx IN ctxHandle, 
  attrind IN BOOLEAN := TRUE);

Introduced in Oracle9i Release 2 (9.2.0.2).

useNullAttributeIndicator is a shortcut for setNullHandling(ctx, NULL_ATTR).

PROCEDURE

setBindValue(
  ctx IN ctxHandle, 
  bindVariableName IN VARCHAR2, 
  bindValue IN VARCHAR2);

Introduced in Oracle Database 10g Release 1 (10.0.1).

Sets bind value for the bind variable appearing in the query string associated with the context handle. The query string with bind variables cannot be executed until all the bind variables are set values using setBindValue() call.

PROCEDURE

clearBindValue(ctx IN ctxHandle);

Introduced in Oracle Database 10g Release 1 (10.0.1).

Clears all the bind values for all the bind variables appearing in the query string associated with the context handle. Afterwards, all the bind variables have to rebind new values using setBindValue() call.


DBMS_XMLGEN Examples

Example 16-24 DBMS_XMLGEN: Generating Simple XML

This example creates an XML document by selecting employee data from an object-relational table and putting the resulting CLOB value into a table.

CREATE TABLE temp_clob_tab(result CLOB);

DECLARE
  qryCtx DBMS_XMLGEN.ctxHandle;
  result CLOB;
BEGIN
  qryCtx := DBMS_XMLGEN.newContext('SELECT * FROM hr.employees');
  -- Set the row header to be EMPLOYEE
  DBMS_XMLGEN.setRowTag(qryCtx, 'EMPLOYEE');
  -- Get the result
  result := DBMS_XMLGEN.getXML(qryCtx);
  INSERT INTO temp_clob_tab VALUES(result);
  --Close context
  DBMS_XMLGEN.closeContext(qryCtx);
END;
/

This query example generates the following XML (only part of the result is shown):

SELECT * FROM temp_clob_tab WHERE ROWNUM = 1;

RESULT
-------------------------------------------------------
<?xml version="1.0"?>
<ROWSET>
 <EMPLOYEE>
  <EMPLOYEE_ID>100</EMPLOYEE_ID>
  <FIRST_NAME>Steven</FIRST_NAME>
  <LAST_NAME>King</LAST_NAME>
  <EMAIL>SKING</EMAIL>
  <PHONE_NUMBER>515.123.4567</PHONE_NUMBER>
  <HIRE_DATE>17-JUN-87</HIRE_DATE>
  <JOB_ID>AD_PRES</JOB_ID>
  <SALARY>24000</SALARY>
  <DEPARTMENT_ID>90</DEPARTMENT_ID>
 </EMPLOYEE>

...
 
1 row selected.

Example 16-25 DBMS_XMLGEN: Generating Simple XML with Pagination (fetch)

Instead of generating all the XML data for all rows, you can use the fetch interface of DBMS_XMLGEN to retrieve a fixed number of rows each time. This speeds up response time and can help in scaling applications that need a Document Object Model (DOM) Application Program Interface (API) on the resulting XML, particularly if the number of rows is large.

The following example uses package DBMS_XMLGEN to retrieve results from table hr.employees:

-- Create a table to hold the results
CREATE TABLE temp_clob_tab(result clob);
DECLARE
  qryCtx DBMS_XMLGEN.ctxHandle;
  result CLOB;
BEGIN
  -- Get the query context;
  qryCtx := DBMS_XMLGEN.newContext('SELECT * FROM hr.employees');
  -- Set the maximum number of rows to be 2
  DBMS_XMLGEN.setMaxRows(qryCtx, 2);
  LOOP
    -- Get the result
    result := DBMS_XMLGEN.getXML(qryCtx);
    -- If no rows were processed, then quit
    EXIT WHEN DBMS_XMLGEN.getNumRowsProcessed(qryCtx) = 0;
 
    -- Do some processing with the lob data
    --   Here, we insert the results into a table.
    --   You can print the lob out, output it to a stream,
    --   put it in a queue, or do any other processing.
    INSERT INTO temp_clob_tab VALUES(result);
  END LOOP;
  --close context
  DBMS_XMLGEN.closeContext(qryCtx);
END;
/

SELECT * FROM temp_clob_tab WHERE rownum <3;

RESULT
----------------------------------------------------------
<?xml version="1.0"?>
<ROWSET>
 <ROW>
  <EMPLOYEE_ID>100</EMPLOYEE_ID>
  <FIRST_NAME>Steven</FIRST_NAME>
  <LAST_NAME>King</LAST_NAME>
  <EMAIL>SKING</EMAIL>
  <PHONE_NUMBER>515.123.4567</PHONE_NUMBER>
  <HIRE_DATE>17-JUN-87</HIRE_DATE>
  <JOB_ID>AD_PRES</JOB_ID>
  <SALARY>24000</SALARY>
  <DEPARTMENT_ID>90</DEPARTMENT_ID>
 </ROW>
 <ROW>
  <EMPLOYEE_ID>101</EMPLOYEE_ID>
  <FIRST_NAME>Neena</FIRST_NAME>
  <LAST_NAME>Kochhar</LAST_NAME>
  <EMAIL>NKOCHHAR</EMAIL>
  <PHONE_NUMBER>515.123.4568</PHONE_NUMBER>
  <HIRE_DATE>21-SEP-89</HIRE_DATE>
  <JOB_ID>AD_VP</JOB_ID>
  <SALARY>17000</SALARY>
  <MANAGER_ID>100</MANAGER_ID>
  <DEPARTMENT_ID>90</DEPARTMENT_ID>
 </ROW>
</ROWSET>
 
<?xml version="1.0"?>
<ROWSET>
 <ROW>
  <EMPLOYEE_ID>102</EMPLOYEE_ID>
  <FIRST_NAME>Lex</FIRST_NAME>
  <LAST_NAME>De Haan</LAST_NAME>
  <EMAIL>LDEHAAN</EMAIL>
  <PHONE_NUMBER>515.123.4569</PHONE_NUMBER>
  <HIRE_DATE>13-JAN-93</HIRE_DATE>
  <JOB_ID>AD_VP</JOB_ID>
  <SALARY>17000</SALARY>
  <MANAGER_ID>100</MANAGER_ID>
  <DEPARTMENT_ID>90</DEPARTMENT_ID>
 </ROW>
 <ROW>
  <EMPLOYEE_ID>103</EMPLOYEE_ID>
  <FIRST_NAME>Alexander</FIRST_NAME>
  <LAST_NAME>Hunold</LAST_NAME>
  <EMAIL>AHUNOLD</EMAIL>
  <PHONE_NUMBER>590.423.4567</PHONE_NUMBER>
  <HIRE_DATE>03-JAN-90</HIRE_DATE>
  <JOB_ID>IT_PROG</JOB_ID>
  <SALARY>9000</SALARY>
  <MANAGER_ID>102</MANAGER_ID>
  <DEPARTMENT_ID>60</DEPARTMENT_ID>
 </ROW>
</ROWSET>
 
2 rows selected.

Example 16-26 DBMS_XMLGEN: Generating Nested XML With Object Types

This example uses object types to represent nested structures.

CREATE TABLE new_departments(department_id   NUMBER PRIMARY KEY,
                             department_name VARCHAR2(20));
CREATE TABLE new_employees(employee_id       NUMBER PRIMARY KEY,
                           last_name         VARCHAR2(20),
                           department_id     NUMBER REFERENCES new_departments);
CREATE TYPE emp_t AS OBJECT("@employee_id"   NUMBER,
                            last_name        VARCHAR2(20));
/
INSERT INTO new_departments VALUES(10, 'SALES');
INSERT INTO new_departments VALUES(20, 'ACCOUNTING');
INSERT INTO new_employees   VALUES(30, 'Scott', 10);
INSERT INTO new_employees   VALUES(31, 'Mary',  10);
INSERT INTO new_employees   VALUES(40, 'John',  20);
INSERT INTO new_employees   VALUES(41, 'Jerry', 20);
COMMIT;
CREATE TYPE emplist_t AS TABLE OF emp_t;
/
CREATE TYPE dept_t AS OBJECT("@department_id" NUMBER,
                             department_name  VARCHAR2(20),
                             emplist          emplist_t);
/
CREATE TABLE temp_clob_tab(result CLOB);
DECLARE
  qryCtx DBMS_XMLGEN.ctxHandle;
  result CLOB;
BEGIN
  DBMS_XMLGEN.setRowTag(qryCtx, NULL);
  qryCtx := DBMS_XMLGEN.newContext
    ('SELECT dept_t(department_id,
                    department_name,
                    CAST(MULTISET
                           (SELECT e.employee_id, e.last_name
                              FROM new_employees e
                              WHERE e.department_id = d.department_id)
                         AS emplist_t))
        AS deptxml
        FROM new_departments d');
  -- now get the result
  result := DBMS_XMLGEN.getXML(qryCtx);
  INSERT INTO temp_clob_tab VALUES (result);
  -- close context
  DBMS_XMLGEN.closeContext(qryCtx);
END;
/
SELECT * FROM temp_clob_tab;

Here is the resulting XML:

RESULT
--------------------------------------------
<?xml version="1.0"?>
<ROWSET>
 <ROW>
  <DEPTXML department_id="10">
   <DEPARTMENT_NAME>SALES</DEPARTMENT_NAME>
   <EMPLIST>
    <EMP_T employee_id="30">
     <LAST_NAME>Scott</LAST_NAME>
    </EMP_T>
    <EMP_T employee_id="31">
     <LAST_NAME>Mary</LAST_NAME>
    </EMP_T>
   </EMPLIST>
  </DEPTXML>
 </ROW>
 <ROW>
  <DEPTXML department_id="20">
   <DEPARTMENT_NAME>ACCOUNTING</DEPARTMENT_NAME>
   <EMPLIST>
    <EMP_T employee_id="40">
     <LAST_NAME>John</LAST_NAME>
    </EMP_T>
    <EMP_T employee_id="41">
     <LAST_NAME>Jerry</LAST_NAME>
    </EMP_T>
   </EMPLIST>
  </DEPTXML>
 </ROW>
</ROWSET>
 
1 row selected.

With relational data, the result is an XML document without nested elements. To obtain nested XML structures, you can use object-relational data, where the mapping is as follows:

  • Object types map as an XML element – see Chapter 5, "XML Schema Storage and Query: Basic".

  • Attributes of the type, map to sub-elements of the parent element

    Note:

    Complex structures can be obtained by using object types and creating object views or object tables. A canonical mapping is used to map object instances to XML.

    When used in column names or attribute names, the at-sign (@) is translated into an attribute of the enclosing XML element in the mapping.

Example 16-27 DBMS_XMLGEN: Generating Nested XML With User-Defined Datatype Instances

When you provide a user-defined datatype instance to DBMS_XMLGEN functions, the user-defined datatype instance is mapped to an XML document using canonical mapping: the attributes of the user-defined datatype are mapped to XML elements. Attributes with names starting with an at-sign (@) are mapped to attributes of the preceding element.

User-defined datatype instances can be used for nesting in the resulting XML document. For example, consider tables, emp and dept:

CREATE TABLE dept(deptno NUMBER PRIMARY KEY, dname VARCHAR2(20));
CREATE TABLE emp(empno   NUMBER PRIMARY KEY, ename VARCHAR2(20), 
                 deptno  NUMBER REFERENCES dept);

To generate a hierarchical view of the data, that is, departments with employees in them, you can define suitable object types to create the structure inside the database as follows:

-- empno is preceded by an at-sign (@) to indicate that it must 
-- be mapped as an attribute of the enclosing Employee element. 
CREATE TYPE emp_t AS OBJECT("@empno" NUMBER,  -- empno defined as attribute
                             ename   VARCHAR2(20));
/
INSERT INTO DEPT VALUES(10, 'Sports');
INSERT INTO DEPT VALUES(20, 'Accounting');
INSERT INTO EMP VALUES(200, 'John',  10);
INSERT INTO EMP VALUES(300, 'Jack',  10);
INSERT INTO EMP VALUES(400, 'Mary',  20);
INSERT INTO EMP VALUES(500, 'Jerry', 20);
COMMIT;
CREATE TYPE emplist_t AS TABLE OF emp_t;
/
CREATE TYPE dept_t AS OBJECT("@deptno" NUMBER, 
                             dname     VARCHAR2(20),
                             emplist   emplist_t);
/
-- Department type dept_t contains a list of employees.
-- We can now query the employee and department tables and get 
-- the result as an XML document, as follows:
CREATE TABLE temp_clob_tab(result CLOB);
DECLARE
  qryCtx DBMS_XMLGEN.ctxHandle;
  RESULT CLOB;
BEGIN
  -- get query context
  qryCtx := DBMS_XMLGEN.newContext(
    'SELECT dept_t(deptno,
                   dname,
                   CAST(MULTISET(SELECT empno, ename
                                   FROM emp e
                                   WHERE e.deptno = d.deptno)
                        AS emplist_t))
       AS deptxml
       FROM dept d');
  -- set maximum number of rows to 5
  DBMS_XMLGEN.setMaxRows(qryCtx, 5);
  -- set no row tag for this result, since there is a single ADT column
  DBMS_XMLGEN.setRowTag(qryCtx, NULL);
  LOOP 
    -- get result
    result := DBMS_XMLGEN.getXML(qryCtx);
    -- if there were no rows processed, then quit
    EXIT WHEN DBMS_XMLGEN.getNumRowsProcessed(qryCtx) = 0;
    -- do something with the result
    INSERT INTO temp_clob_tab VALUES (result);
  END LOOP;
END;
/

Function MULTISET treats the employees working in the department as a list, and function CAST assigns this list to the appropriate collection type. A department instance is created, and DBMS_XMLGEN routines create the XML for the object instance.

SELECT * FROM temp_clob_tab;

RESULT
---------------------------------
<?xml version="1.0"?>
<ROWSET>
 <DEPTXML deptno="10">
  <DNAME>Sports</DNAME>
  <EMPLIST>
   <EMP_T empno="200">
    <ENAME>John</ENAME>
   </EMP_T>
   <EMP_T empno="300">
    <ENAME>Jack</ENAME>
   </EMP_T>
  </EMPLIST>
 </DEPTXML>
 <DEPTXML deptno="20">
  <DNAME>Accounting</DNAME>
  <EMPLIST>
   <EMP_T empno="400">
    <ENAME>Mary</ENAME>
   </EMP_T>
   <EMP_T empno="500">
    <ENAME>Jerry</ENAME>
   </EMP_T>
  </EMPLIST>
 </DEPTXML>
</ROWSET>
 
1 row selected.

The default name ROW is not present because we set that to NULL. The deptno and empno have become attributes of the enclosing element.

Example 16-28 DBMS_XMLGEN: Generating an XML Purchase Order

This example uses DBMS_XMLGEN.getXMLType() to generate a purchase order in XML format using object views.

-- Create relational schema and define object views
-- DBMS_XMLGEN maps user-defined datatype attribute names that start
--    with an at-sign (@) to XML attributes
 
-- Purchase Order Object View Model
 
-- PhoneList varray object type
CREATE TYPE phonelist_vartyp AS VARRAY(10) OF VARCHAR2(20)
/
-- Address object type
CREATE TYPE address_typ AS OBJECT(Street VARCHAR2(200),
                                  City   VARCHAR2(200),
                                  State  CHAR(2),
                                  Zip    VARCHAR2(20))
/
-- Customer object type
CREATE TYPE customer_typ AS OBJECT(CustNo    NUMBER,
                                   CustName  VARCHAR2(200),
                                   Address   address_typ,