Search code examples
oracleplsqlxmltype

XMLType in oracle not generating tags for columns with Null values


I am trying to Convert the below table rows to XMLtype and then to clob

ID, PROJ_NO
1   Proj1
2   (null)
3   Proj5

I used the below Query to convert each row to xml

 select xmltype( cursor(Select * from PROJ_TEST_DEMO where id= 1  )).getclobval() from  dual

Result is

"<?xml version="1.0"?>
<ROWSET>
 <ROW>
  <ID>1</ID>
  <PROJ_NO>Proj1</PROJ_NO>
 </ROW>
</ROWSET>
"

While

select xmltype( cursor(Select * from PROJ_TEST_DEMO where id=3  ) ).getclobval() from  dual

gives

"<?xml version="1.0"?>
<ROWSET>
 <ROW>
  <ID>3</ID>
 </ROW>
</ROWSET>
"

Is there a option to stop xmltype from excluding null value columns


Solution

  • You can use dbms_xmlgen with dbms_xmlgen.setNullHandling(qryCtx, dbms_xmlgen.EMPTY_TAG) or dbms_xmlgen.NULL_ATTR:

    For example, create own function

    create or replace function f_get_xmltype_with_nulls (cur sys_refcursor, null_handling int default dbms_xmlgen.null_attr)
      return xmltype
    as
      /* null_handling may be: 
          DROP_NULLS CONSTANT NUMBER:= 0;  Leaves out the tag for NULL elements.
          NULL_ATTR CONSTANT NUMBER:= 1; (Default) Sets xsi:nil="true".
          EMPTY_TAG CONSTANT NUMBER:= 2; Sets, for example, <foo/>.
      */
      res xmltype;
      lc dbms_xmlgen.ctxhandle;
    begin
      lc:=dbms_xmlgen.newcontext(cur);
      -- you can replace null_attr with empty_tag here:
      dbms_xmlgen.setnullhandling(lc, null_handling);
      res:=dbms_xmlgen.getxmltype(lc);
      return res;
    end;
    /
    

    then you can use it in queries:

    SQL> select f_get_xmltype_with_nulls(cursor(select null x from dual connect by level<10)) x from dual;
    
    X
    ------------------------------------------------------------------------
    <ROWSET xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance">
     <ROW>
      <X xsi:nil = "true"/>
     </ROW>
     <ROW>
      <X xsi:nil = "true"/>
     </ROW>
     <ROW>
      <X xsi:nil = "true"/>
     </ROW>
     <ROW>
      <X xsi:nil = "true"/>
     </ROW>
     <ROW>
      <X xsi:nil = "true"/>
     </ROW>
     <ROW>
      <X xsi:nil = "true"/>
     </ROW>
     <ROW>
      <X xsi:nil = "true"/>
     </ROW>
     <ROW>
      <X xsi:nil = "true"/>
     </ROW>
     <ROW>
      <X xsi:nil = "true"/>
     </ROW>
    </ROWSET>
    

    As you can see second parameter of this function is null_handling:

    • DROP_NULLS CONSTANT NUMBER:= 0; Leaves out the tag for NULL elements.
    • NULL_ATTR CONSTANT NUMBER:= 1; (Default) Sets xsi:nil="true".
    • EMPTY_TAG CONSTANT NUMBER:= 2; Sets, for example, .

    Or you can even inline your function into the query:

    with 
       function f_get_xmltype_with_nulls (cur sys_refcursor, null_handling int default dbms_xmlgen.null_attr)
         return xmltype
       as
         /* null_handling may be: 
             DROP_NULLS CONSTANT NUMBER:= 0;  Leaves out the tag for NULL elements.
             NULL_ATTR CONSTANT NUMBER:= 1; (Default) Sets xsi:nil="true".
             EMPTY_TAG CONSTANT NUMBER:= 2; Sets, for example, <foo/>.
         */
         res xmltype;
         lc dbms_xmlgen.ctxhandle;
       begin
         lc:=dbms_xmlgen.newcontext(cur);
         -- you can replace null_attr with empty_tag here:
         dbms_xmlgen.setnullhandling(lc, null_handling);
         res:=dbms_xmlgen.getxmltype(lc);
         return res;
       end;
    select
       f_get_xmltype_with_nulls(cursor(select null as x from dual)) as xxx 
    from dual
    /
    

    Result with default NULL_ATTR:

    XXX
    -----------------------------------------------------------------
    <ROWSET xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance">
     <ROW>
      <X xsi:nil = "true"/>
     </ROW>
    </ROWSET>
    

    Result with default EMPTY_TAG:

    select
       f_get_xmltype_with_nulls(cursor(select null as x from dual),2) as xxx 
    from dual;
    
    XXX
    -------------------------------------
    <ROWSET>
     <ROW>
      <X/>
     </ROW>
    </ROWSET>