1

Need to create one generic stored procedure which will take table name as input parameter and create xml file with below format. Xml file name should be table name.

    <XML>
    <TABLENAME></TABLENAME>
    <RECORDS>
    <RECORD>
              <COLNAME>AAA</COLNAME>
              <COLNAME>AAA</COLNAME>
    <RECORD>
    <RECORD>
              <COLNAME>AAA</COLNAME>
              <COLNAME>AAA</COLNAME>
    <RECORD>
    <RECORD>
              <COLNAME>AAA</COLNAME>
              <COLNAME>AAA</COLNAME>
    <RECORD>
    <RECORD>
              <COLNAME>AAA</COLNAME>
              <COLNAME>AAA</COLNAME>
    <RECORD>
    <RECORD>
              <COLNAME>AAA</COLNAME>
              <COLNAME>AAA</COLNAME>
    <RECORD>
    </RECORDS>
    </XML> 


<TABLENAME>  = actual table name(case should be same as in database)
<COLNAME> = actual column names from table(case should be same as in database)
Keep other tags as it is. Repeat <RECORD> tag for each row retrieved.
hardik rawal
  • 117
  • 1
  • 2
  • 18

1 Answers1

5

Oracle has a built-in function to get the contents of a table as XML:

create table t42(id number, str varchar2(10));
insert into t42 values (1, 'AA');
insert into t42 values (2, 'BB');

select dbms_xmlgen.getxmltype('select * from t42')
from dual;

DBMS_XMLGEN.GETXMLTYPE('SELECT*FROMT42')
----------------------------------------
<ROWSET>
 <ROW>
  <ID>1</ID>
  <STR>AA</STR>
 </ROW>
 <ROW>
  <ID>2</ID>
  <STR>BB</STR>
 </ROW>
</ROWSET>

You can add your own tags around that; could be done as a query but since you want a stored procedure:

create or replace function table_to_xml(table_name in varchar2) return xmltype as
  xml xmltype;
begin
  select xmlelement("XML",
      xmlelement(evalname(table_name),
        dbms_xmlgen.getxmltype('select * from "' || table_name || '"')))
  into xml
  from dual;

  return xml;
end table_to_xml;
/

select table_to_xml('T42') from dual;

TABLE_TO_XML('T42')
----------------------------------------
<XML><T42><ROWSET>
  <ROW>
    <ID>1</ID>
    <STR>AA</STR>
  </ROW>
  <ROW>
    <ID>2</ID>
    <STR>BB</STR>
  </ROW>
</ROWSET>
</T42></XML>

So this has the structure you want (well, I think, but see below), but has ROWSET and ROW instead of RECORDS and RECORD. That might not matter, it depends whether you're developing the format for this interface still. If it does matter then you can apply a further step to rename those nodes, or - more helpfully - use the dbms_xmlgen procedures setrowsettag and setrowtag, which is simple in your procedure (and demonstrated below).

I'm assuming what you showed as <TABLENAME></TABLENAME> was a mistake, and you want the records within that tag. If not, and you really do want that for some reason, change the query in the function to:

  select xmlelement("XML",
      xmlconcat(xmlelement(evalname(table_name), null),
      dbms_xmlgen.getxmltype('select * from "' || table_name || '"')))
  into xml
  from dual;

You can then write that out to a file any way you normally would; if you're calling from SQL*Plus etc. you could select and spool, or if you don't want it returned at all you could add UTL_FILE directive to write the file from within the procedure, but that would have to be to a directory object on the DB server, which might not be convenient.

Mostly for my own benefit as I don't do a lot with XML:

create or replace procedure table_to_xml_file(table_name in varchar2) as
  ctx dbms_xmlgen.ctxhandle;
  clb clob;
  file utl_file.file_type;
  buffer varchar2(32767);
  position pls_integer := 1;
  chars pls_integer := 32767;
begin
  ctx := dbms_xmlgen.newcontext('select * from "' || table_name || '"');
  dbms_xmlgen.setrowsettag(ctx, 'RECORDS');
  dbms_xmlgen.setrowtag(ctx, 'RECORD');

  select xmlserialize(document
        xmlelement("XML",
          xmlelement(evalname(table_name),
            dbms_xmlgen.getxmltype(ctx)))
      indent size = 2)
  into clb
  from dual;

  dbms_xmlgen.closecontext(ctx);

  file := utl_file.fopen('<directory>', table_name || '.xml', 'w', 32767);
  while position < dbms_lob.getlength(clb) loop
    dbms_lob.read(clb, chars, position, buffer);
    utl_file.put(file, buffer);
    utl_file.fflush(file);
    position := position + chars;
  end loop;
  utl_file.fclose(file);
end table_to_xml_file;
/

When run with exec table_to_xml_file('T42'), this produces a file called T42.xml in the server directory pointed to by the <directory> directory object, which contains:

<XML>
  <T42>
    <RECORDS>
      <RECORD>
        <ID>1</ID>
        <STR>AA</STR>
      </RECORD>
      <RECORD>
        <ID>2</ID>
        <STR>BB</STR>
      </RECORD>
    </RECORDS>
  </T42>
</XML>

Incidentally, I've put double-quotes around the table name in the select inside the dbms_xmlgen.getxmltype call. That's to meet the 'case should be same as in database' requirement for the table name; it has to be passed to the procedure in the correct case or it will error. That's simpler than trying to correct the case within the procedure somehow, which would be awkward, or impossible if you had two tables with the same name apart from the case. The columns names will be in the correct case anyway.

Community
  • 1
  • 1
Alex Poole
  • 183,384
  • 11
  • 179
  • 318
  • hi, when i had ran above SP then error shows --> Connecting to the database DBold. ORA-29280: invalid directory path ORA-06512: at "SYS.UTL_FILE", line 41 ORA-06512: at "SYS.UTL_FILE", line 478 ORA-06512: at "DBO_CTIPS17FEB.TABLE_TO_XML_FILE", line 42 ORA-06512: at line 6 Process exited. Disconnecting from the database DBold. – hardik rawal Feb 25 '14 at 20:12
  • Connecting to the database CTIPS_old. ORA-29280: invalid directory path ORA-06512: at "SYS.UTL_FILE", line 41 ORA-06512: at "SYS.UTL_FILE", line 478 ORA-06512: at "DBO_CTIPS17FEB.TABLE_TO_XML_FILE", line 45 ORA-06512: at line 6 Process exited. Disconnecting from the database CTIPS_old. – hardik rawal Feb 25 '14 at 20:25
  • @hardikrawal - did you replace `` with an actual directory object name, which has a valid real path on the server? As shown in the `all_directories` view? – Alex Poole Feb 25 '14 at 20:54
  • yes , i put correct path , i create DIR and give all read write rights to user and its shows in select * from all_directories; ---> file := utl_file.fopen('E:\myxml\', table_name || '.xml', 'w', 32767); – hardik rawal Feb 25 '14 at 21:03
  • Hi,I has solved all errors, change Begin like this --> xsl VARCHAR2(800); begin -- from http://stackoverflow.com/a/7802924 xsl := '..... – hardik rawal Feb 25 '14 at 21:23
  • OK, glad you got it working; but it should work either way, and not sure what that had to do with your ORA-29280 error *8-) – Alex Poole Feb 25 '14 at 21:48
  • due to clob variable all table data not shows in XML .. can we change that. – hardik rawal Feb 26 '14 at 04:20
  • hi, when i put one table name then its shows error .Connecting to the database DB_old. ORA-31061: XDB error: special char to escaped char conversion failed. Process exited. Disconnecting from the database DB_old how i can resolved – hardik rawal Feb 26 '14 at 09:21
  • @hardikrawal - the `clob` issue was because I wasn't flushing the file; you can't have more than 32k of `put` data without a flush. But this will still only work if your `clob` values have line breaks, which is probably the case. I've also simplified the code to not need the XSL transformation. – Alex Poole Feb 26 '14 at 10:22
  • @hardikrawal - for the ORA-31061, [this sounds like the same issue](http://stackoverflow.com/q/7270445/266304), but I see you already saw that one. Apparently you have dodgy data in that table? You need to fix the data really, but first you'll need to identify it, and probably figure out how/why it's wrong. Possibly a character set issue somewhere . – Alex Poole Feb 26 '14 at 10:27
  • i have some table which column have some special char so this type of error shows, can handle this char at query level, i don't want to change data So. we can use view, temp tables or etc and using like --> regexp_replace(unistr(PROJ_DESC), ''[[:punct:]]'','''') as PROJ_DESC etc. please help me – hardik rawal Feb 26 '14 at 11:27
  • @hardikrawal - sorry, not sure why punctuation would be a problem. If you have to do that translation then you might have to tailor the `newcontext` call for those tables, instead of doing `select *`. I don't see an obvious simple way to do generally, but then I don't really understand the problem. If you can't do that then I guess you should ask a new question. – Alex Poole Feb 26 '14 at 12:20
  • ok, i ask a new Queation Check --> http://stackoverflow.com/questions/22041833/generate-xml-file-from-oracle-table – hardik rawal Feb 26 '14 at 12:46
  • hi, i have been ran above store procedure(as per your code) for my table its run but xml file not tables data value its shows all data are missing – hardik rawal Mar 01 '14 at 19:25
  • @hardikrawal - that implies the table has no data; or your `newcontext` has a `where` clause that is finding no data. – Alex Poole Mar 01 '14 at 19:30
  • i check and found that "clb" variable not get any table values – hardik rawal Mar 01 '14 at 19:38
  • select xmlserialize(document xmlelement("XML", xmlelement(evalname(table_name), dbms_xmlgen.getxmltype(ctx))) indent size = 2) into clb from dual; this query gives result thats means its not get data – hardik rawal Mar 01 '14 at 20:03
  • when I ran above Store Procedure then i got below error when generating xml file ora-30625 method dispatch on null self argument is disallowed – hardik rawal Mar 04 '14 at 10:23
  • @hardikrawal - are you sure you're actually populating `ctx` and that the query within it is valid and finds data? You can check if `ctx` is null before that `select`. You had some conditions around setting it in another question, maybe they are not right. – Alex Poole Mar 04 '14 at 10:27
  • when I use above Store procedure then it shows err --- ORA-04030: out of process memory when trying to allocate 1016 bytes (qmxlu subheap,qmemNextBuf:alloc) ORA-06512: at "DBO_CTIPS_19FEB2014.TABLE_TO_XML_FILE", line 185 ORA-06512: at line 6 – hardik rawal Mar 05 '14 at 12:27