In part one of “Generating XML from SQL and PL/SQL“, I explained how to generate XML from SQL. In this second part I’ll show you how you can generate XML from table data in PL/SQL.
The datatype to hold XML in PL/SQL or in the database is XMLTYPE. So you can use the generated XML in PL/SQL, store it in table (XMLTYPE column), transform it to a CLOB (using the XMLTYPE getClobVal member function which I use in the examples) and write it to a file.
XMLTYPE
The easiest way to create an XML document, is using the constructor of XMLTYPE. This constructor can have several datatypes as input, like a CLOB and VARCHAR2, but as we’re going to base our XML on table data, we’re using a REF CURSOR.
You can create a ref cursor and pass on this ref cursor to the XMLTYPE constructor like this:
DECLARE
l_refcursor SYS_REFCURSOR;
l_xmltype XMLTYPE;
BEGIN
OPEN l_refcursor FOR SELECT department_id
, department_name
FROM departments
WHERE department_id IN (10,20);
l_xmltype := XMLTYPE(l_refcursor);
dbms_output.put_line(l_xmltype.getClobVal);
END;
/
<?xml version="1.0"?> <ROWSET> <ROW> <DEPARTMENT_ID>10</DEPARTMENT_ID> <DEPARTMENT_NAME>Administration</DEPARTMENT_NAME> </ROW> <ROW> <DEPARTMENT_ID>20</DEPARTMENT_ID> <DEPARTMENT_NAME>Marketing</DEPARTMENT_NAME> </ROW> </ROWSET>
As you can see it creates a simple XML document where each row is seperated with a ROW tag.
DBMS_XMLGEN
The DBMS_XMLGEN built-in is similar to the XMLTYPE constructor, but accepts a query directly:
DECLARE
l_xmltype XMLTYPE;
BEGIN
l_xmltype := dbms_xmlgen.getxmltype('SELECT department_id
, department_name
FROM departments
WHERE department_id IN (10,20)'
);
dbms_output.put_line(l_xmltype.getClobVal);
END;
/
<ROWSET> <ROW> <DEPARTMENT_ID>10</DEPARTMENT_ID> <DEPARTMENT_NAME>Administration</DEPARTMENT_NAME> </ROW> <ROW> <DEPARTMENT_ID>20</DEPARTMENT_ID> <DEPARTMENT_NAME>Marketing</DEPARTMENT_NAME> </ROW> </ROWSET>
But it also provides procedures to change the ROWSET and ROW tags.
DECLARE
l_xmltype XMLTYPE;
l_ctx dbms_xmlgen.ctxhandle;
BEGIN
l_ctx := dbms_xmlgen.newcontext('SELECT department_id
, department_name
FROM departments
WHERE department_id in (10,20)'
);
dbms_xmlgen.setrowsettag(l_ctx, 'Departments');
dbms_xmlgen.setrowtag(l_ctx, 'Dept');
l_xmltype := dbms_xmlgen.getXmlType(l_ctx) ;
dbms_xmlgen.closeContext(l_ctx);
dbms_output.put_line(l_xmltype.getClobVal);
End;
/
<Departments> <Dept> <DEPARTMENT_ID>10</DEPARTMENT_ID> <DEPARTMENT_NAME>Administration</DEPARTMENT_NAME> </Dept> <Dept> <DEPARTMENT_ID>20</DEPARTMENT_ID> <DEPARTMENT_NAME>Marketing</DEPARTMENT_NAME> </Dept> </Departments>
dbms_xmldom
With the XMLTYPE constructor and DBMS_XMLGEN package, you can create simple XML documents, fast and easy. When you need to create more advanced XML documents or want to have more control on how your XML document looks like, DBMS_XMLDOM can be used. The DBMS_XMLDOM package is a bit more complicated as you’ll have to create the entire document by calling functions and procedures of the package.
The following example creates an XML document with the department information retrieved from the query. In short, this is how it works: create new elements and add them as a (child) node.
DECLARE
l_xmltype XMLTYPE;
l_domdoc dbms_xmldom.DOMDocument;
l_root_node dbms_xmldom.DOMNode;
l_department_element dbms_xmldom.DOMElement;
l_departments_node dbms_xmldom.DOMNode;
l_dept_element dbms_xmldom.DOMElement;
l_dept_node dbms_xmldom.DOMNode;
l_name_element dbms_xmldom.DOMElement;
l_name_node dbms_xmldom.DOMNode;
l_name_text dbms_xmldom.DOMText;
l_name_textnode dbms_xmldom.DOMNode;
l_location_element dbms_xmldom.DOMElement;
l_location_node dbms_xmldom.DOMNode;
l_location_text dbms_xmldom.DOMText;
l_location_textnode dbms_xmldom.DOMNode;
BEGIN
-- Create an empty XML document
l_domdoc := dbms_xmldom.newDomDocument;
-- Create a root node
l_root_node := dbms_xmldom.makeNode(l_domdoc);
-- Create a new node Departments and add it to the root node
l_department_element := dbms_xmldom.createElement(l_domdoc, 'Deptartments' );
l_departments_node := dbms_xmldom.appendChild(l_root_node,dbms_xmldom.makeNode(l_department_element));
FOR r_dept IN (SELECT dept.department_id
, dept.department_name
, loc.city
FROM departments dept
JOIN locations loc
ON loc.location_id = dept.location_id
WHERE dept.department_id IN (10,20)
)
LOOP
-- For each record, create a new Dept element with the Department ID as attribute.
-- and add this new Dept element to the Departments node
l_dept_element := dbms_xmldom.createElement(l_domdoc, 'Dept' );
dbms_xmldom.setAttribute(l_dept_element, 'DeptID', r_dept.department_id);
l_dept_node := dbms_xmldom.appendChild(l_departments_node,dbms_xmldom.makeNode(l_dept_element));
-- Each Dept node will get a Name node which contains the department name as text
l_name_element := dbms_xmldom.createElement(l_domdoc, 'Name' );
l_name_node := dbms_xmldom.appendChild(l_dept_node,dbms_xmldom.makeNode(l_name_element));
l_name_text := dbms_xmldom.createTextNode(l_domdoc, r_dept.department_name );
l_name_textnode := dbms_xmldom.appendChild(l_name_node,dbms_xmldom.makeNode(l_name_text));
-- Each Dept node will aslo get a Location node which contains the location(city) as text
l_location_element := dbms_xmldom.createElement(l_domdoc, 'Location' );
l_location_node := dbms_xmldom.appendChild(l_dept_node,dbms_xmldom.makeNode(l_location_element));
l_location_text := dbms_xmldom.createTextNode(l_domdoc, r_dept.city );
l_location_textnode := dbms_xmldom.appendChild(l_location_node,dbms_xmldom.makeNode(l_location_text));
END LOOP;
l_xmltype := dbms_xmldom.getXmlType(l_domdoc);
dbms_xmldom.freeDocument(l_domdoc);
dbms_output.put_line(l_xmltype.getClobVal);
END;
/
<Deptartments> <Dept DeptID="10"> <Name>Administration</Name> <Location>Seattle</Location> </Dept> <Dept DeptID="20"> <Name>Marketing</Name> <Location>Toronto</Location> </Dept> </Deptartments>
In this second example I added the employees to each department. I created a new cursor loop on employees inside the department cursor loop, so that the employees of that department are added in a child node of the department. I also changed some code: I cast(dbms_xmldom.makeNode) the elements directly to a node, this way I need less variables:
DECLARE
l_domdoc dbms_xmldom.DOMDocument;
l_xmltype XMLTYPE;
l_root_node dbms_xmldom.DOMNode;
l_departments_node dbms_xmldom.DOMNode;
l_dept_element dbms_xmldom.DOMElement;
l_dept_node dbms_xmldom.DOMNode;
l_name_node dbms_xmldom.DOMNode;
l_name_textnode dbms_xmldom.DOMNode;
l_location_node dbms_xmldom.DOMNode;
l_location_textnode dbms_xmldom.DOMNode;
l_employees_node dbms_xmldom.DOMNode;
l_emp_element dbms_xmldom.DOMElement;
l_emp_node dbms_xmldom.DOMNode;
l_emp_first_name_node dbms_xmldom.DOMNode;
l_emp_first_name_textnode dbms_xmldom.DOMNode;
l_emp_last_name_node dbms_xmldom.DOMNode;
l_emp_last_name_textnode dbms_xmldom.DOMNode;
BEGIN
-- Create an empty XML document
l_domdoc := dbms_xmldom.newDomDocument;
-- Create a root node
l_root_node := dbms_xmldom.makeNode(l_domdoc);
-- Create a new node Departments and add it to the root node
l_departments_node := dbms_xmldom.appendChild( l_root_node
, dbms_xmldom.makeNode(dbms_xmldom.createElement(l_domdoc, 'Deptartments' ))
);
FOR r_dept IN (SELECT dept.department_id
, dept.department_name
, loc.city
FROM departments dept
JOIN locations loc
ON loc.location_id = dept.location_id
WHERE dept.department_id IN (10,20)
)
LOOP
-- For each record, create a new Dept element with the Department ID as attribute.
-- and add this new Dept element to the Departments node
l_dept_element := dbms_xmldom.createElement(l_domdoc, 'Dept' );
dbms_xmldom.setAttribute(l_dept_element, 'Deptno', r_dept.Department_Id );
l_dept_node := dbms_xmldom.appendChild( l_departments_node
, dbms_xmldom.makeNode(l_dept_element)
);
-- Each Dept node will get a Name node which contains the department name as text
l_name_node := dbms_xmldom.appendChild( l_dept_node
, dbms_xmldom.makeNode(dbms_xmldom.createElement(l_domdoc, 'Name' ))
);
l_name_textnode := dbms_xmldom.appendChild( l_name_node
, dbms_xmldom.makeNode(dbms_xmldom.createTextNode(l_domdoc, r_dept.department_name ))
);
-- Each Dept node will aslo get a Location node which contains the location(city) as text
l_location_node := dbms_xmldom.appendChild( l_dept_node
, dbms_xmldom.makeNode(dbms_xmldom.createElement(l_domdoc, 'Location' ))
);
l_location_textnode := dbms_xmldom.appendChild( l_location_node
, dbms_xmldom.makeNode(dbms_xmldom.createTextNode(l_domdoc, r_dept.city ))
);
-- For each department, add an Employees node
l_employees_node := dbms_xmldom.appendChild( l_dept_node
, dbms_xmldom.makeNode(dbms_xmldom.createElement(l_domdoc, 'Employees' ))
);
FOR r_emp IN (SELECT employee_id
, first_name
, last_name
FROM employees
WHERE department_id = r_dept.department_id
)
LOOP
-- For each record, create a new Emp element with the Employee ID as attribute.
-- and add this new Emp element to the Employees node
l_emp_element := dbms_xmldom.createElement(l_domdoc, 'Emp' );
dbms_xmldom.setAttribute(l_emp_element, 'empid', r_emp.employee_id );
l_emp_node := dbms_xmldom.appendChild( l_employees_node
, dbms_xmldom.makeNode(l_emp_element)
);
-- Each emp node will get a First name and Last name node which contains the first name and last name as text
l_emp_first_name_node := dbms_xmldom.appendChild( l_emp_node
, dbms_xmldom.makeNode(dbms_xmldom.createElement(l_domdoc, 'FirstName' ))
);
l_emp_first_name_textnode := dbms_xmldom.appendChild( l_emp_first_name_node
, dbms_xmldom.makeNode(dbms_xmldom.createTextNode(l_domdoc, r_emp.first_name ))
);
l_emp_last_name_node := dbms_xmldom.appendChild( l_emp_node
, dbms_xmldom.makeNode(dbms_xmldom.createElement(l_domdoc, 'LastName' ))
);
l_emp_last_name_textnode := dbms_xmldom.appendChild( l_emp_last_name_node
, dbms_xmldom.makeNode(dbms_xmldom.createTextNode(l_domdoc, r_emp.last_name ))
);
END LOOP;
END LOOP;
l_xmltype := dbms_xmldom.getXmlType(l_domdoc);
dbms_xmldom.freeDocument(l_domdoc);
dbms_output.put_line(l_xmltype.getClobVal);
END;
/
This is how the final example looks, with the employees working for the department added in the XML document:
<Deptartments>
<Dept Deptno="10">
<Name>Administration</Name>
<Location>Seattle</Location>
<Employees>
<Emp empid="200">
<FirstName>Jennifer</FirstName>
<LastName>Whalen</LastName>
</Emp>
</Employees>
</Dept>
<Dept Deptno="20">
<Name>Marketing</Name>
<Location>Toronto</Location>
<Employees>
<Emp empid="201">
<FirstName>Michael</FirstName>
<LastName>Hartstein</LastName>
</Emp>
<Emp empid="202">
<FirstName>Pat</FirstName>
<LastName>Fay</LastName>
</Emp>
</Employees>
</Dept>
</Deptartments>
As you can see DBMS_XMLDOM is the most advanced way to create XML documents from table data, but it can also be used to create a document from different sources (query, CLOB, …), to search in XML documents, and to change documents (e.g. add nodes to an existing document).
More info:











16 Comments
Khalid Mehmood
30/11/2012
Nice work. . .
Xavier
04/12/2012
Simply Excellent...
Marco Gralike
04/12/2012
Not to be annoying, liked your posts, but hereby some pointers anyway.
- getClobVal is a deprecated operator and should be avoided in favor or XMLSerialize
- DBMS_XMLGEN is not really maintained anymore by the XMLDB dev team and, IMHO, is only useful for some encoding/decoding functionality it still has and sometimes still would be a valid reason to use it. If not only debugging statements fed into DBMS_XMLGEN can be a xxxx in the xxx and if unlucky can not be optimized by the CBO because its treated as a string and not XML/SQL and/or...
- DBMS_XMLDOM is still a valid way of doing stuff in the PL/SQL realm of things although the XMLDB Dev team strongly advices to use the XMLELEMENT, XMLFOREST, etc functions from your first post OR (better) switch to the W3C XML compliant standard of using XQuery.
:-)
Vladimir Przyjalkowski
05/12/2012
"- getClobVal is a deprecated operator and should be avoided in favor or XMLSerialize"
Unfortunately XMLSERIALIZE and XMLCAST don't work in PL/SQL expressions, only in SQL ones ...
Sonia
20/03/2013
Hi Marco thank you for your precisions. When you say "XMLDB Dev team strongly advices to use the XMLELEMENT" do you have a link for me I mean a reference where I can read it?
The problem is we have a similar discussion at work. I' m working on a xml projekt and have to generate xml-file from the database. Therefor I used the sql/xml Library but an consultant told us that it is the wrong way. So that we have to rewrite all of that.
Thanks a lot.
Gert Poel
04/12/2012
Thanks for the response Marco.
I didn't know getClobVal is deprecated and I don't see it in the Oracle documentation(http://docs.oracle.com/cd/E11882_01/appdev.112/e25788/t_xml.htm#ARPLS71964).
To be honest, I don't use dbms_xmlgen much, besides to generate some quick XML documents.
SQL functions like XMLElement and XMLForest used in a view definition is very handy to create XML from table data.
I have my own PL/SQL package to manipulate XML and it's based on DBMS_XMLDOM, so to create and manipulate more complicated XML documents, I use this one.
But as this article is a small introduction and for some people DBMS_XMLGEN can be a good start to work with XML from PL/SQL.
And no, you're not annoying ;-)
Vladimir Przyjalkowski
05/12/2012
A couple of more examples.
XML data including XML Schema:
VARIABLE clob CLOB
VARIABLE sqltext VARCHAR2 ( 4000 )
EXECUTE :sqltext := -
'SELECT deptno AS "@deptno", dname AS "name", loc AS "loc" FROM dept'
BEGIN
:clob := DBMS_XMLQUERY.GETXML ( :sqltext, DBMS_XMLQUERY.SCHEMA );
END;
/
You may use DBMS_XMLQUERY.DTD constant otherwise as well as SQL:
VARIABLE dtd NUMBER
EXECUTE :dtd := DBMS_XMLQUERY.DTD
SELECT
DBMS_XMLQUERY.GETXML ( :sqltext, :dtd ) AS "XML text with DTD"
FROM dual
;
Example for Java (really not PL/SQL):
import java.sql.DriverManager;
import java.sql.Connection;
import oracle.xml.sql.query.OracleXMLQuery;
import oracle.jdbc.driver.OracleDriver;
public class OracleXMLQueryPrimer {
public static void main ( String args [ ] ) throws Exception {
DriverManager.registerDriver ( new OracleDriver ( ) );
Connection conn =
DriverManager.getConnection ( "jdbc:oracle:oci:scott/tiger@" );
OracleXMLQuery query =
new OracleXMLQuery ( conn, "SELECT * FROM dept" );
System.out.println ( query.getXMLString ( ) );
conn.close ( );
}
}
Kjell Forsberg
09/12/2012
I'm generating XML from table data using:
SELECT XMLElement("messages",XMLAttributes(maxnr as "batch"),
(SELECT XMLAgg(XMLElement("message",
XMLAttributes('VPS' as "type",'A' as "action", ROWNUM as "id", to_char(sysdate,'YYYY-MM-DD') || 'T' || to_char(sysdate,'HH24:MI:SS') || '+02:00' as "date_time"),
and so on.
This works fine on Oracle 9 client sqlpusw but on Oracle 10 client sqlplusw it dosn't format and gets useless.
Do you have any idea how to solve this?
Marco Gralike
19/12/2012
There is no reason guys, to concat the date strings, have a look at the following example
WITH datestuff AS (SELECT xmltype('<date>2011-05-23T12:01:51.217+02:00</date>') xmlcol FROM dual ) SELECT to_timestamp_tz(xt.datum,'YYYY-MM-DD"T"HH24:MI:SS.FF9tzh:tzm') AS "TO_TIMESTAMP_TZ" FROM datestuff , xmltable('*' passing xmlcol COLUMNS datum varchar2(35) PATH '/date' ) xt; TO_TIMESTAMP_TZ ----------------------------------- 23-05-11 12:01:51,217000000 +02:00Richard
10/12/2012
SQL> SELECT XMLElement("Departments"
2 , XMLAgg(XMLElement("Dept", XMLAttributes(a.deptid as "Deptid")
3 , XMLElement("Description", a.descr)
4 , XMLElement("Location", a.location)
5 ) )
6 ) html
7 from ps_dept_tbl a
8 where a.deptid like '433%'
9 and a.effdt = (select max(a2.effdt) from ps_dept_tbl a2
10 where a2.setid = a.setid
11 and a2.deptid = a.deptid)
12 /
HTML
-----------------------------------------------------------------------------------------------------------
<Departments>
<Dept Deptid="4335">
<Description>US Business Support</Description>
<Location>A0000</Location>
</Dept>
<Dept Deptid="4333">
<Description>Inter-Company</Description>
<Location>A0098</Location>
</Dept>
<Dept Deptid="4337">
<Description>Sales and Trading Support</Description>
<Location>A0098</Location>
</Dept>
<Dept Deptid="4338">
<Description>Middle Office Other</Description>
<Location>A0098</Location>
</Dept>
<Dept Deptid="4339">
<Description>STF DLG and Collateral Mgt</Description>
<Location>A0098</Location>
</Dept>
<Dept Deptid="4331">
<Description>NB Fixed Inc Trading</Description>
<Location>A0098</Location>
</Dept>
<Dept Deptid="4332">
<Description>NB Sales&Trading Syndicate</Description>
<Location>A0098</Location>
</Dept>
<Dept Deptid="4334">
<Description>BC PC Financing</Description>
<Location>A0098</Location>
</Dept>
<Dept Deptid="4336">
<Description>BC - Management</Description>
<Location>A0098</Location>
</Dept>
<Dept Deptid="4330">
<Description>STM Bank Funding</Description>
<Location>A0098</Location>
</Dept>
</Departments>
Kjell Forsberg
12/12/2012
Hi Richard,
Yes this is what you would expect, and this is what I get in Oracle 9 client.
In 10 and 11 I get not formatted text "streaming" and in column 81 there's a new line.
In your case there's a new line after > in proper places, bud not in mine.
I did solve it with a trick getting no new lina at all.
Regards
Kjell
Richard
13/12/2012
Kjell
Running in 10g
SELECT XMLElement("messages",XMLAttributes('maxnr' as "batch"),
(SELECT XMLAgg(XMLElement("message",
XMLAttributes('VPS' as "type",'A' as "action", ROWNUM as "id", to_char(sysdate,'YYYY-MM-DD') || 'T' || to_char(sysdate,'HH24:MI:SS') || '+02:00' as "date_time"), 'apple')) from dual))
xml_column from dual
SQL> set long 100000
SQL> /
XML_COLUMN
----------------------------------------------------------------------------------------------------
<messages batch="maxnr">
<message type="VPS" action="A" id="1" date_time="2012-12-13T10:40:03+02:00">apple</message>
</messages>
SQL> set long 80
SQL> /
XML_COLUMN
-------------------------------------------------------------------------------
<messages batch="maxnr">
<message type="VPS" action="A" id="1" date_time="2012
Could I see a sample of your output and perhaps the sql script?
Gert Poel
14/12/2012
Kjell,
try using xmlserialize.
Set your linesize to 1000(at least big enough ;-)).
SELECT xmlserialize(content XMLElement( "messages"
, XMLAttributes('maxnr' as "batch")
, (SELECT XMLAgg(XMLElement("message"
, XMLAttributes( 'VPS' as "type"
, 'A' as "action"
, ROWNUM as "id"
, to_char(sysdate,'YYYY-MM-DD') || 'T' || to_char(sysdate,'HH24:MI:SS') || '+02:00' as "date_time"
)
, 'apple'
)
)
FROM dual
)
)
AS varchar2(4000) indent size=2) xml_column
FROM dual
/
This wil result in this output:
XML_COLUMN
----------------------------------------------------------------------------------------------------
<messages batch="maxnr">
<message type="VPS" action="A" id="1" date_time="2012-12-14T11:02:52+02:00">apple</message>
</messages>
Regards,
Gert
Ove Olsen
16/12/2012
Hello Gert.
Nice blog post, enjoyed reading it.
Ive been doing similar things for a customer althou we had a more object oriented aproach wich I hope im allowed to share with you guys.
Demo 1:
Example of the usage of object types as containers for xml.
These objects can be created to reflect some xsd, wich again can be used to validate the structure.
If you chose to use the Oracle XDB Repository to store and reference xsd make sure to patch up to Oracle 11.2.0.3 or later. You will avoid some bugs then.
call dbms_output.enable(2000000);
set serveroutput on
set echo on
create or replace type demo_person as object(
"@id" varchar2(100) -- attribute
, "navn" varchar2(100) -- lower case element
, "aDrEsSe" varchar2(100) -- mixed case element
, mobil varchar2(100) -- default upper case element
, lonn varchar2(100) -- default upper case element
, constructor function demo_person return self as result
, member function getxmltype return xmltype
);
/
create or replace type body demo_person
is
constructor function demo_person return self as result
is
begin
self."@id" := null;
self."navn" := null;
self."aDrEsSe" := null;
self.mobil := null;
self.lonn := null;
return;
end;
member function getxmltype return xmltype
is
begin
return xmltype(self);
end;
end;
/
pause
declare
l_person demo_person;
begin
l_person := new demo_person('1','Ove Olsen','Norway','12345678',1000); --notice the new keyword...
dbms_output.put_line(l_person.getxmltype().extract('/').getStringVal()); -- using extract to get pretty print xml
end;
/
pause
drop type demo_person force;
-- creating an "abstract" type, can not be instantiated
create or replace type demo_person as object(
navn varchar2(100)
, adresse varchar2(100)
, mobil varchar2(100)
, lonn varchar2(100)
, member function getxmltype return xmltype
) not final not instantiable;
/
--Creating a type using the abstract
create or replace type demo_ansatt under demo_person(
ansatt_id varchar2(100)
, constructor function demo_ansatt return self as result
, overriding member function getxmltype return xmltype -- notice the overriding keyword..
) not final;
/
create or replace type demo_leder under demo_ansatt(
leder_id varchar2(100)
, constructor function demo_leder return self as result
, overriding member function getxmltype return xmltype
);
/
create or replace type body demo_ansatt
is
constructor function demo_ansatt return self as result
is
begin
self.ansatt_id := null;
self.navn := null;
self.adresse := null;
self.mobil := null;
self.lonn := null;
return;
end;
overriding member function getxmltype return xmltype
is
begin
return xmltype(self);
end hent_xmltype;
end;
/
create or replace type body demo_leder
is
constructor function demo_leder return self as result
is
begin
self.leder_id := null;
self.ansatt_id := null;
self.navn := null;
self.adresse := null;
self.mobil := null;
self.lonn := null;
return;
end;
overriding member function getxmltype return xmltype
is
begin
return xmltype(self);
end hent_xmltype;
end;
/
create or replace type demo_ansatt_liste as table of demo_ansatt;
/
create or replace type demo_avdeling as object(
avdeling_navn varchar2(100)
, avdeling_sted varchar2(100)
, avdeling_leder demo_leder
, ansatt_liste demo_ansatt_liste
, constructor function demo_avdeling return self as result
, member procedure legg_til_ansatt(p_ansatt in demo_ansatt)
, member function getxmltype return xmltype
);
/
create or replace type body demo_avdeling
is
constructor function demo_avdeling return self as result
is
begin
self.avdeling_navn := null;
self.avdeling_sted := null;
self.avdeling_leder := new demo_leder();
self.ansatt_liste := new demo_ansatt_liste();
return;
end;
member procedure legg_til_ansatt(p_ansatt in demo_ansatt)
is
begin
if self.ansatt_liste is null then
self.ansatt_liste := new demo_ansatt_liste();
end if;
self.ansatt_liste.extend();
self.ansatt_liste( self.ansatt_liste.last() ) := p_ansatt;
end;
member function getxmltype return xmltype
is
begin
return xmltype(self);
end;
end;
/
declare
l_avd demo_avdeling;
l_leder demo_leder;
l_ansatt demo_ansatt;
begin
l_avd := new demo_avdeling();
l_avd.avdeling_navn := 'Dataess';
l_avd.avdeling_sted := 'Norway';
l_ansatt := new demo_ansatt();
l_ansatt.ansatt_id := '2';
l_ansatt.navn := 'Ove Olsen';
l_ansatt.adresse := 'Oslo';
l_ansatt.mobil := '12345678';
l_ansatt.lonn := '1000';
l_avd.legg_til_ansatt(l_ansatt);
l_leder := new demo_leder();
l_leder.leder_id := '1';
l_leder.ansatt_id := '1';
l_leder.navn := 'Bjorn Deveril-Mathisen';
l_leder.adresse := 'Oslo';
l_leder.mobil := '12345678';
l_leder.lonn := '1000';
l_avd.avdeling_leder := l_leder;
l_ansatt := new demo_ansatt();
l_ansatt.ansatt_id := '3';
l_ansatt.navn := 'Pierre Vidal';
l_ansatt.adresse := 'Oslo';
l_ansatt.mobil := '12345677';
l_ansatt.lonn := '1000';
l_avd.legg_til_ansatt(l_ansatt);
dbms_output.put_line(l_avd.getxmltype().extract('/').getclobval());
end;
/
set echo off
drop type demo_avdeling force;
drop type demo_ansatt_liste force;
drop type demo_leder force;
drop type demo_ansatt force;
drop type demo_person force;
Demo 2:
Lets try to serialize xml to object type...
call dbms_output.enable(2000000);
set serveroutput on
set echo on
-- Creating a global type spec
create or replace type demo_person as object(
navn varchar2(100)
, adresse varchar2(100)
, mobil varchar2(100)
, lonn number(17,3)
, constructor function demo_person return self as result
, member function getxmltype return xmltype
);
/
-- Creating a global type body
create or replace type body demo_person
is
constructor function demo_person return self as result
is
begin
self.navn := null;
self.adresse := null;
self.mobil := null;
self.lonn := null;
return;
end;
member function getxmltype return xmltype
is
begin
return xmltype(self);
end;
end;
/
pause
--Filling the global type with some data and printing it as xml.
declare
l_person demo_person;
l_xml xmltype;
begin
l_person := new demo_person();
l_person.navn := 'Ove Olsen';
l_person.adresse := 'Norway';
l_person.mobil := '12345678';
l_person.lonn := 10000;
l_xml := l_person.getxmltype();
dbms_output.put_line(l_xml.extract('/').getClobVal());
end;
/
pause
-- Serializing some xml to a global object
declare
l_person demo_person;
l_xml xmltype;
begin
l_xml := xmltype('<DEMO_PERSON>
<NAVN>Ove Olsen</NAVN>
<ADRESSE>Norway</ADRESSE>
<MOBIL>12345678</MOBIL>
<LONN>10000</LONN>
</DEMO_PERSON>');
xmltype.toobject(l_xml,l_person);
dbms_output.put_line( l_person.navn || ',' ||
l_person.adresse || ',' ||
l_person.mobil || ',' ||
l_person.lonn);
end;
/
pause
drop type demo_person force;
This oo aproach might be a little but unorthodox but it is very easy to maintain and change the structure of the xml if that is needed. The external contracts might change (wsdl) etc.
Gert Poel
18/12/2012
Hi Ove,
thanks for sharing your solution!
Somehow some strange things ended up in your code: there's two times hent_xmltype that should be getxmltype.
Regard,
Gert
Ove Olsen
18/12/2012
Hi Gert,
Yes I noticed after posting but then there was no edit link for me to correct it.
The "hent_xmltype" is the original "Norwegianated" function name, i did a very quick translate as we go along to english, and i kinda missed out those.
Generating XML from SQL & PL/SQL and Code Instrumentation « iAdvise blog
04/12/2012
[...] Part two, in PL/SQL [...]
No trackbacks yet.