Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Dan.

Asked: January 21, 2009 - 1:25 pm UTC

Last updated: January 21, 2009 - 2:41 pm UTC

Version: 10.2.0.4

Viewed 1000+ times

You Asked

Hi Tom,

I have been trying to get "nice" looking XMLType output in 10g, and have been running into strange inconsistencies with how Oracle seems to treat the datatype. I was wondering if you could shed some light on what's going on behind the scenes to cause this.

Setup:
SET LONG 9999;
 
drop table t;
 
create table t as (
select 1 id, 'bob' name, 7 num from dual UNION ALL
select 2 id, 'dan' name, 5 num from dual UNION ALL
select 3 id, 'jim' name, 6 num from dual);


First of all, what doesn't work:
select XMLElement("Doc",
         XMLAGG(XMLELEMENT("ID",
           XMLForest(name as "NAME",
                     num as "Number"))))
from t;
 
XMLELEMENT("DOC",XMLAGG(XMLELEMENT("ID",XMLFOREST(NAMEAS"NAME",NUMAS"NUMBER"))))
-------------------------------------------------------------------------------------------------------------------------------------------------
<Doc><ID><NAME>bob</NAME><Number>7</Number></ID><ID><NAME>dan</NAME><Number>5</Number></ID><ID><NAME>jim</NAME><Number>6</Number></ID></Doc>
 
1 rows selected


Now using a table:
drop table tmp_t;
create table tmp_t (data XMLTYPE);
 
insert into tmp_t
select XMLElement("Doc",
         XMLAGG(XMLELEMENT("ID",
           XMLForest(name as "NAME",
                     num as "Number"))))
from t;
                     
select data from tmp_t;
 
drop table tmp_t succeeded.
create table succeeded.
1 rows inserted
DATA                                                                                                                                                                                                                                                                          
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
<Doc><ID><NAME>bob</NAME><Number>7</Number></ID><ID><NAME>dan</NAME><Number>5</Number></ID><ID><NAME>jim</NAME><Number>6</Number></ID></Doc>
 
1 rows selected


Also doesn't work. But now I wrap that in an XMLElement...
select XMLElement("SuperDoc",
         data) from tmp_t;
 
XMLELEMENT("SUPERDOC",DATA)
---------------------------------------------------
<SuperDoc><Doc>
  <ID>
    <NAME>bob</NAME>
    <Number>7</Number>
  </ID>
  <ID>
    <NAME>dan</NAME>
    <Number>5</Number>
  </ID>
  <ID>
    <NAME>jim</NAME>
    <Number>6</Number>
  </ID>
</Doc>
</SuperDoc>
 
 
1 rows selected


This is where I start to get confused. The "inner" part of this XML got formatted in "pretty" form, but the new element I introduced didn't - it's simply concatenated on, without proper indenting.

Now, I was able to eventually find the .extract() method, which is the correct way to pretty print in 10g from what I understand. However, it doesn't work directly:

select data.extract('/*') from tmp_t;


Error starting at line 1 in command:
select data.extract('/*') from tmp_t
Error at Command Line:1 Column:7
Error report:
SQL Error: ORA-00904: "DATA"."EXTRACT": invalid identifier
00904. 00000 -  "%s: invalid identifier"
*Cause:    
*Action:


Instead it has to be specifically cast as XMLType, even though that's the variable type it is:

select XMLType('' || data).extract('/*') from tmp_t;

XMLTYPE(''||DATA).EXTRACT('/*')                                                                                                               
---------------------------------------------------------------------------------------------------------------------------------------------
<Doc>
  <ID>
    <NAME>bob</NAME>
    <Number>7</Number>
  </ID>
  <ID>
    <NAME>dan</NAME>
    <Number>5</Number>
  </ID>
  <ID>
    <NAME>jim</NAME>
    <Number>6</Number>
  </ID>
</Doc>

1 rows selected


So my questions are:
1. Why does Oracle seem to treat XMLType inconsistently when it comes to pretty printing? Shouldn't an XMLType column be the same as an XMLType returned by a function?
2. Why can't I just call extract() directly, when my column type is XMLType?

Thanks,

-Dan

and Tom said...

Frankly - they are all valid. XML is free form, there is no indenting, spacing, whatever.

We are not pretty printing XMLType differently. XMLType prints whatever was put in, if you put in your xmlelement output, that is what you'll get back by default:

ops$tkyte%ORA10GR2> select data from tmp_t;

DATA
-------------------------------------------------------------------------------
<Doc><ID><NAME>bob</NAME><Number>7</Number></ID><ID><NAME>dan</NAME><Number>5</
Number></ID><ID><NAME>jim</NAME><Number>6</Number></ID></Doc>


when you applied extract() to it, that pretty printed it.

And by calling xmlelement("SuperDoc", data), you implicitly extracted it - and then xmlelemented it (which just returns an xmltype) and we know xmlelement just glues tags on.


as for extract, that is an idiosyncrasy of objects, you have to use a correlation name:


ops$tkyte%ORA10GR2> select t.data.extract('/*') from tmp_t t;

T.DATA.EXTRACT('/*')
-------------------------------------------------------------------------------
<Doc>
  <ID>
    <NAME>bob</NAME>
    <Number>7</Number>
  </ID>
  <ID>
    <NAME>dan</NAME>
    <Number>5</Number>
  </ID>
  <ID>
    <NAME>jim</NAME>
    <Number>6</Number>
  </ID>
</Doc>



and if you have relational data, you can then:

ops$tkyte%ORA10GR2> select XMLElement("Doc",
  2           XMLAGG(XMLELEMENT("ID",
  3             XMLForest(name as "NAME",
  4                       num as "Number")))).extract('/*') data
  5  from t;

DATA
-------------------------------------------------------------------------------
<Doc>
  <ID>
    <NAME>bob</NAME>
    <Number>7</Number>
  </ID>
  <ID>
    <NAME>dan</NAME>
    <Number>5</Number>
  </ID>
  <ID>
    <NAME>jim</NAME>
    <Number>6</Number>
  </ID>
</Doc>



to pretty print it.

but in short:

xmlelement - just returns valid xml stuff, formatting isn't part of xml.

xmltype - just returns what you sent in by default, send in xmlelement without any spacing and that is what you get back.

ops$tkyte%ORA10GR2> insert into tmp_t (data) values ( '
  2  <x>
  3     <y>
  4       Hello
  5     </y>
  6  </x>' );

1 row created.

ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> select data from tmp_t;

DATA
-------------------------------------------------------------------------------
<Doc><ID><NAME>bob</NAME><Number>7</Number></ID><ID><NAME>dan</NAME><Number>5</
Number></ID><ID><NAME>jim</NAME><Number>6</Number></ID></Doc>


<x>
   <y>
     Hello
   </y>
</x>




the extract method, as documented, does any and all "pretty printing"

http://docs.oracle.com/cd/B19306_01/appdev.102/b14259/xdb04cre.htm#sthref469

Rating

  (2 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Aha...

Dan, January 21, 2009 - 2:58 pm UTC

"...And by calling xmlelement("SuperDoc", data), you implicitly extracted it ..."

This is what I was missing - that explains the behavior exactly. I'm not sure why it would be implicitly extracted in that situation, but at least I know why it's doing what it does.

Thanks.

Extracting data from XMLTYPE column

Suresh, January 26, 2009 - 10:37 am UTC

Tom,

Hope this falls under the same subject thread and hope you will answer.

I have a xml file with 2 records ( for testing).
<ROWSET>
<stat><V>6.4</V><sid>durhems01</sid><di>2009.01.14</di><hi>20</hi><ti>1231964700
</ti><lv>o</lv><id>arhmanbue--01r</id><vars><var><k>cpu_used._total</k><u>%</u><
fr>245</fr><n>1.000000</n><l>CPU Used (All)</l><t>number</t></var></vars></stat>
<stat><V>6.4</V><sid>durhems01</sid><di>2009.01.14</di><hi>20</hi><ti>1231964700
</ti><lv>o</lv><id>arhmanbue--01r</id><vars><var><k>cpu_used._total</k><u>%</u><
fr>245</fr><n>1.000000</n><l>CPU Used (All)</l><t>number</t></var></vars></stat>
</ROWSET>

I loaded this into xmltype column.  

SQL> CREATE TABLE po_xml_tab (
        poid number(10),
        poDoc XMLType );
grant read on directory XML_DIR to public with grant option;

3) Create Function to read from OS file into an XMLTYPE column

SQL> 
Create OR Replace Function getClobDocument(
  filename in varchar2,
  charset  in varchar2 default NULL)
return CLOB deterministic
is
    file            bfile := bfilename('XML_DIR',filename);
    charContent     CLOB := ' ';
    targetFile      bfile;
    lang_ctx        number := DBMS_LOB.default_lang_ctx;
    charset_id      number := 0;
    src_offset      number := 1 ;
    dst_offset      number := 1 ;
    warning         number;
begin
   if charset is not null then
       charset_id := NLS_CHARSET_ID(charset);
   end if;
   targetFile := file;
   DBMS_LOB.fileopen(targetFile, DBMS_LOB.file_readonly);
   DBMS_LOB.LOADCLOBFROMFILE(charContent, targetFile, DBMS_LOB.getLength(targetFile), 
                             src_offset, dst_offset, charset_id, lang_ctx,warning);
   DBMS_LOB.fileclose(targetFile);
   return charContent;
 end;

 -------------
 INSERT INTO po_xml_tab VALUES(100, XMLType(getCLOBDocument('kkk.xml')));
 




SELECT 
  extract(w.poDoc,'//k[1]/text()').getStringVal()
     "value"
  FROM po_xml_tab w where poid=100

I get a result that is :
cpu_used._totalcpu_used._total

I thought the result would show up for two records:

cpu_used._total
cpu_used._total


Is it something not correct here?

Thanks.