Could Not Get Columns with XMLTABLE in Oracle PL/SQL - oracle

I try to get values from columns of a XML file.
I receive XML file which in my function is assigned to c_xml but for the purposes of this question is represented in the variable c_xml in the code below.
The problem is that I have not anything printed in DBMS_OUTPUT. PUT_LINE, so I could not get any values from XML file and I could not go further with my developing.
It would be great if somebody could help to understand where is the problem with extracting values from this XML. Thanks for your time :)
The code is written on Oracle PL/SQL and follows:
DECLARE
c_xml xmltype;
BEGIN
c_xml :=
xmltype
('<?xml version=''1.0'' encoding=''utf-8''?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Header/>
<env:Body>
<srvc:returnActStateByEgnResponse xmlns="http://curr_state_egn/CURR_STATE_EGNService" xmlns:srvc="http://curr_state_egn/CURR_STATE_EGNServiceService">
<srvc:result>
<consents_tblType>
<item>
<req_id>112</req_id>
<purpose_code>CC0100</purpose_code>
<consent_state>0</consent_state>
</item>
<item>
<req_id>112</req_id>
<purpose_code>CC0200</purpose_code>
<consent_state>1</consent_state>
</item>
<item>
<req_id>112</req_id>
<purpose_code>CC0300</purpose_code>
<consent_state>0</consent_state>
</item>
</consents_tblType>
</srvc:result>
</srvc:returnActStateByEgnResponse>
</env:Body>
</env:Envelope>');
FOR consents_tblTypes IN
( SELECT
p_req_id
, p_purpose_code
, p_consent_state
FROM xmltable(
XMLNamespaces(
'http://schemas.xmlsoap.org/soap/envelope/' AS "env"
--, 'http://www.w3.org/2001/XMLSchema-instance' AS "xsi"
, 'http://curr_state_egn/CURR_STATE_EGNServiceService' AS "srvc"
),
'/env:Envelope/env:Body/srvc:returnActStateByEgnResponse/srvc:result/consents_tblType/item'
PASSING c_xml
COLUMNS
p_req_id NUMBER PATH 'req_id' --/text()
, p_purpose_code VARCHAR2(20) PATH 'purpose_code' --/text()
, p_consent_state NUMBER PATH 'consent_state' --/text()
)
)
LOOP
DBMS_OUTPUT.put_line('p_req_id = ' || to_char(consents_tblTypes.p_req_id)) ;
DBMS_OUTPUT.put_line('p_purpose_code = ' || consents_tblTypes.p_purpose_code) ;
DBMS_OUTPUT.put_line('p_consent_state = ' || to_char(consents_tblTypes.p_consent_state)) ;
END LOOP;
end;

Default namespace has to be included in the declaration.
XMLNamespaces('http://schemas.xmlsoap.org/soap/envelope/' AS "env"
, 'http://curr_state_egn/CURR_STATE_EGNServiceService' AS "srvc"
, default 'http://curr_state_egn/CURR_STATE_EGNService')
This statment xmlns="http://curr_state_egn/CURR_STATE_EGNService" changes defult namespaces.

Related

How to return Table as a row using Oracle XML DB Webservices

I am trying to expose an Oracle Table as a web service using XML DB Service.
I want to return multiple rows of table based on where condition passed in the Request of the web service.
Following -: native-oracle-xml-db-web-services-11gr1
A procedure is developed which takes PK column as input, and returns column as output. It works when return is column by column.
But when I try to return the complete row as type, it is not working.
This is working.
URL of webservice -: http://domain:8080/orawsv/TEST/GET_TEST_TAB?wsdl
create or replace PROCEDURE GET_TEST_TAB (
p_id IN test_tab.id%TYPE,
p_description OUT test_tab.description%type) AS
BEGIN
SELECT description into p_description FROM test_tab
WHERE id = p_id;
END GET_TEST_TAB;
Now I want to return the multiple rows of TEST_TAB table, without hard coding the column name.
I have modified the procedure as
CREATE OR replace PROCEDURE P_TableAsWS_XMLIN_XMLOUT (
p_in IN XMLTYPE,
p_out OUT XMLTYPE
) AS
BEGIN
-- cursor_ OUT SYS_REFCURSOR) AS
-- with data as
--(select '<a><c>1</c><c>2</c></a>' xmlval
-- from dual)
SELECT
XMLELEMENT(
"employees",XMLAGG(XMLELEMENT(
"employee",XMLFOREST(e.id AS "empno",e.description AS "ename")
) )
)
INTO p_out
FROM
test_tab e,
XMLTABLE ( '/inp/*' PASSING p_in
COLUMNS
id varchar(50) PATH '/id'
) inp
WHERE
e.id = inp.id ;
end P_TableAsWS_XMLIN_XMLOUT;
Passing Input -:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:p="http://xmlns.oracle.com/orawsv/TEST/P_TABLEASWS_XMLIN_XMLOUT">
<soapenv:Header/>
<soapenv:Body>
<p:P_TABLEASWS_XMLIN_XMLOUTInput>
<p:P_OUT-XMLTYPE-OUT/>
<p:P_IN-XMLTYPE-IN>
<inp>
<id>1</id>
<id>2</id>
</inp>
</p:P_IN-XMLTYPE-IN>
</p:P_TABLEASWS_XMLIN_XMLOUTInput>
</soapenv:Body>
</soapenv:Envelope>
Getting Output -:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<P_TABLEASWS_XMLIN_XMLOUTOutput xmlns="http://xmlns.oracle.com/orawsv/TEST/P_TABLEASWS_XMLIN_XMLOUT">
<P_OUT>
<employees>
<employee>
<empno>1</empno>
<ename>ONE</ename>
</employee>
<employee>
<empno>2</empno>
<ename>TWO</ename>
</employee>
</employees>
</P_OUT>
</P_TABLEASWS_XMLIN_XMLOUTOutput>
</soap:Body>
</soap:Envelope>
Problem is -:
a) I have manually added each column in the procedure.
If any new column is added, procedure needs to be modified
b) XMLTABLE output column type has to be hard coded.
Unable to define column as TABLE_NAME.COLUM_NAME%TYPE

How do I pass external function as varible in xpath in oracle XMLTable

I have a xml file like this:
<GroupList>
<Group>
<Title>
<Name>Computer</Name>
<Title>
<Type>
<Name>Book</Name>
</Type>
<DataList>
<DataElement>
<Name>Comment</Name>
<Type>text</Type>
<Value>ABC</Value>
<Ordinality>1</Ordinality>
</DataElement>
<DataElement>
<Name>Location</Name>
<Type>set</Type>
<Value>123</Value>
<Ordinality>1</Ordinality>
</DataElement>
</DataList>
</Group>
</GroupList>
I am trying to pass an external function as a variable in xpath, I need get the DataElment/Name ='Location' values.
here is the query snippet:
SELECT *
FROM wd,
XMLTABLE (
'GroupList/Group[Title/Name=''Computer'']'
PASSING xmltype(wd.data), externalfunction as "i"
COLUMNS
name VARCHAR2(200) PATH 'Title/Name'
,type VARCHAR2(200) PATH 'Type/Name'
,location VARCHAr2(200) PATH 'DataElementList/dataElement[$i]/Name'
,location_value VARCHAR2(200) PATH 'DataElementList/dataElement[$i]/Value'
) x
the external function returns a number, when I run the query, I got this error:
ORA-19112: error raised during evaluation: XVM-01008: [XPST0008] Invalid reference
1 DataElementList/dataElement[$i]/Value
what's the correct way to pass variable i to xpath? Thank you for your help.
Here is below query to get value for one data element , I have issues in external function with parameter i for multiple set ...Error I see you should use DataList/DataElement instead of DataElementList/dataElement[$i]/Value
with mytable as (select xmltype('
<GroupList>
<Group>
<Title>
<Name>Computer</Name>
</Title>
<Type>
<Name>Book</Name>
</Type>
<DataList>
<DataElement>
<Name>Comment</Name>
<Type>text</Type>
<Value>ABC</Value>
<Ordinality>1</Ordinality>
</DataElement>
</DataList>
</Group>
</GroupList>') as xmlcol from dual)
SELECT *
FROM mytable,
XMLTABLE (
'GroupList/Group[Title/Name=''Computer'']'
PASSING xmlcol
COLUMNS
name VARCHAR2(200) PATH 'Title/Name'
,type VARCHAR2(200) PATH 'Type/Name'
,location VARCHAr2(200) PATH 'DataList/DataElement/Name'
,location_value VARCHAR2(200) PATH 'DataList/DataElement/Value'
) x
#Tina not sure how to achieve in single query but I have did through looping having one table. Below are the codes that can be done through procedures ... I just want to share whether it may be helpful
create table DataElement with fields Name, Type,value and Ordinality
PROCEDURE GET_DataElement(
in_xml_frag IN CLOB,
in_table_name IN VARCHAR2,
out_xml_rec OUT DataElement%ROWTYPE)
IS
v_xml_rec DataElement%ROWTYPE;
v_row_value VARCHAR2(100);
v_row_division CLOB ;
v_xml SYS.XMLTYPE := XMLTYPE(in_xml_frag);
CURSOR table_fields_rec
IS
SELECT column_name,
data_length column_size,
data_type,
table_name
FROM all_tab_columns
WHERE TABLE_NAME = in_table_name
ORDER BY column_id ASC;
BEGIN
FOR rec IN table_fields_rec
LOOP
IF (v_xml.existsNode('/' || rec.table_name || '/' || rec.column_name || '/text()') = 1) THEN
v_row_value := v_xml.EXTRACT('/' || rec.table_name || '/' || rec.column_name ||'/text()') .GETSTRINGVAL();
v_row_value := COMMON_TOOLS.ELIMINATE_SPECIAL_CHRCTRS(v_row_value);
CASE rec.column_name
WHEN 'Name' THEN
v_xml_rec.Name := v_row_value;
WHEN 'Type' THEN
v_xml_rec.Type := v_row_value;
WHEN 'value' THEN
v_xml_rec.value := v_row_value;
WHEN 'Ordinality' THEN
v_xml_rec.Ordinality := v_row_value;
ELSE
NULL;
END CASE;
ELSE
NULL;
END IF;
END LOOP;
out_xml_rec := v_xml_rec;
END GET_DataElement;
thank you for the help. I found an alternative without using index for the datalist. If anybody is interested, here is the working query.
SELECT x.*, y.*
FROM wd,
XMLTABLE (
'GroupList/Group[Title/Name=''Computer'']'
PASSING xmltype(wd.data)
COLUMNS
name VARCHAR2(200) PATH 'Title/Name'
,type VARCHAR2(200) PATH 'Type/Name'
,datalist xmltype PATH 'DataList'
) x
, XMLTABLE (
'/DataList/DataElement'
PASSING x.datalist
COLUMNS
location VARCHAr2(200) PATH 'Name'
location_value VARCHAR2(200) PATH 'Value'
) y
where y.location ='Location'
and other conditions

clob to xmltype: converting " to &quote

I am converting clob to xmltype in plsql as below.
xml xmltype := xmltype(Input);
Input variables contains the text as below
<request xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<comment>Hello, this is "james"</comment>
</request>
After the xmltype conversion the text is changed to Hello, this is "james""
When tried to extract the xml from xmltype I need to extract the text as Hello, this is "james".
Xmlcast
select xmlcast(xmltype('<request xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<comment>Hello, this is "james"</comment>
</request>').extract('/request/comment/text()') as varchar2(100) ) from dual;
just especify the charset like this: XMLTYPE(Input,NLS_CHARSET_ID('AL32UTF8'))

Adding an attribute to all nodes matching an XPATH expression using Oracle XML DB

I cannot come to a solution to this task: my goal is to pass a cursor to a PL/SQL procedure and get the results as an XMLType. The function dbms_xmlgen.getxmltype() makes this task straightforward
<ROWSET>
<ROW>
<FIRST_NAME>John</FIRST_NAME>
<LAST_NAME>Goodman</LAST_NAME>
<HIRE_DATE>22-JUN-2011</HIRE_DATE>
</ROW>
</ROWSET>
Now I want to add the cursor column data type as an attribute to each corresponding XML element.
<ROWSET>
<ROW>
<FIRST_NAME type="VARCHAR2">John</FIRST_NAME>
<LAST_NAME type="VARCHAR2">Goodman</LAST_NAME>
<HIRE_DATE type="DATE">22-JUN-2011</HIRE_DATE>
</ROW>
</ROWSET>
This could be done using dynamic SQL, so I can write a PL/SQL function to get an associative array mapping each column to the corresponding data type.
Supposing I have both the aforementioned associativa array and the XMLType, how can I apply a set of transformations using a XPATH expression such as
-- pseudocode ;)
func(myXMLType, '//FIRST_NAME', ?add attribute to the matching node?)
Any other approach to get the job done will be fine
You could convert your metadata information to its own XML representation, and then have an XPath that finds the matching entry:
select *
from xmltable('for $i in $x/ROWSET return (element {"ROWSET"} {
for $j in $i/ROW
return (element {"ROW"} {
for $k in $j/*
return (element {$k/name()} {
attribute type { $m/metadata/column[#name=$k/name()]/#type },
$k/text()
} )
} )
} )'
passing generated_xml as "x", metadata_xml as "m"
columns result xmltype path '.');
Each ROWSET (there's only one, of course) generates a new ROWSET element; then each ROW under that generates a new ROW element; then each mode under that generates a new node with the same name and value, but the name is also used to find the matching entry in the metadata and extract it's type attribute and use it as an attribute for this node instead.
A worked example:
create or replace function cursor_to_xml(p_cursor sys_refcursor) return xmltype is
l_cursor sys_refcursor;
l_ctx dbms_xmlgen.ctxhandle;
l_xmltype xmltype;
l_cursor_num pls_integer;
l_col_cnt pls_integer;
l_desc_tab dbms_sql.desc_tab2;
l_metadata varchar2(32767);
l_result xmltype;
begin
-- get generated XMl as shown in the question
l_cursor := p_cursor;
l_ctx := dbms_xmlgen.newcontext(querystring => l_cursor);
l_xmltype := dbms_xmlgen.getxmltype(ctx => l_ctx);
dbms_xmlgen.closecontext(ctx => l_ctx);
-- use DBMS_SQL to get the data types
l_cursor_num := dbms_sql.to_cursor_number(rc => l_cursor);
dbms_sql.describe_columns2(c => l_cursor_num, col_cnt => l_col_cnt,
desc_t => l_desc_tab);
dbms_sql.close_cursor(l_cursor_num);
-- manually create an XML version of the column name/data type mappings
-- which could be extended easily to include length/scale/precision/etc.
l_metadata := '<metadata>';
for i in 1..l_desc_tab.count loop
l_metadata := l_metadata || '<column name="' || l_desc_tab(i).col_name ||
'" type="' || case l_desc_tab(i).col_type
when 1 then 'VARCHAR2'
when 2 then 'NUMBER'
when 12 then 'DATE'
-- ...
end
|| '"/>';
end loop;
l_metadata := l_metadata || '</metadata>';
-- use XMLTable with an XPath that deconstructs and reconstructs the
-- generated XML to add an attribute with the type; this is passed two
-- XML objects, referred to internally as $x and $m
-- xmlserialize() formats the result with indentation; xmltype then just
-- gets it back to that type - you may not need either really
select xmltype(xmlserialize(document result as varchar2(4000) indent))
into l_result
from xmltable('for $i in $x/ROWSET return (element {"ROWSET"} {
for $j in $i/ROW
return (element {"ROW"} {
for $k in $j/*
return (element {$k/name()} {
attribute type { $m/metadata/column[#name=$k/name()]/#type },
$k/text()
} )
} )
} )'
passing l_xmltype as "x", xmltype(l_metadata) as "m"
columns result xmltype path '.');
return l_result;
end cursor_to_xml;
/
Then an block that generates a cursor - similar to your example but with two rows just to check that works - and then calls the function to get the modified XML:
set serveroutput on;
declare
l_cursor sys_refcursor;
begin
open l_cursor for
select cast('John' as varchar2(10)) as first_name,
cast('Goodman' as varchar2(10)) as last_name,
date '2011-06-22' as hire_date
from dual
union all
select cast('Rhea' as varchar2(10)) as first_name,
cast('Perlman' as varchar2(10)) as last_name,
date '2012-07-23' as hire_date
from dual;
dbms_output.put_line(cursor_to_xml(l_cursor).getstringval);
end;
/
PL/SQL procedure successfully completed.
<ROWSET>
<ROW>
<FIRST_NAME type="VARCHAR2">John</FIRST_NAME>
<LAST_NAME type="VARCHAR2">Goodman</LAST_NAME>
<HIRE_DATE type="DATE">22-JUN-11</HIRE_DATE>
</ROW>
<ROW>
<FIRST_NAME type="VARCHAR2">Rhea</FIRST_NAME>
<LAST_NAME type="VARCHAR2">Perlman</LAST_NAME>
<HIRE_DATE type="DATE">23-JUL-12</HIRE_DATE>
</ROW>
</ROWSET>
You may want more data types defined in the CASE, of course.

how to set the empty tag in generating xml from oracle table

SET PAGES 0;
SET LINE 1000;
SET LONG 9999999;
SPOOL C:\pensionnew.xml;
col foo format a60000
SELECT DBMS_XMLGEN.GETXML('SELECT * FROM DATAAG')foo FROM DUAL;
SPOOL OFF;
i use this code for generating xml.. the answer is:suppose the the column not have value means the tag wont came . i need empty tag for that for that
use the API for it, dont use the quick fire getxml(string) version.
eg:
SQL> variable xml clob;
SQL> declare
2 ctx number;
3 begin
4 dbms_lob.createtemporary(:xml, true, dbms_lob.call);
5 ctx := dbms_xmlgen.newcontext('select * from foo');
6 dbms_xmlgen.setnullhandling(ctx, dbms_xmlgen.EMPTY_TAG);
7 dbms_xmlgen.getxml(ctx, :xml);
8 dbms_xmlgen.closecontext(ctx);
9 end;
10 /
PL/SQL procedure successfully completed.
SQL> print xml
XML
--------------------------------------------------------------------------------
<?xml version="1.0"?>
<ROWSET>
<ROW>
<ID>1</ID>
<A>a</A>
</ROW>
<ROW>
<ID>2</ID>
<A/>
</ROW>
</ROWSET>

Resources