Extracting a node where xmlns is set to blank - sql

I'm having difficulties extracting the value from certain nodes in an XML structure using XMLTABLE. Below query works perfectly when you remove the xmlns="" attribute from the SubListItem nodes. And as you can see, the XML already has a default namespace. I honestly have no clue how I can deal with this "blanking out" of the namespace on certain nodes like this.
For further clarification, the creation of this XML is not within my control and is provided by a third-party. I've also changed the names of the nodes and the content from the delivered files while preserving the structure of the XML.
SELECT f.airline, f.flightnumber, fl.gate
FROM xmltable(
xmlnamespaces(
default 'http://some/name.space',
'http://www.w3.org/2001/XMLSchema' as "xsd",
'http://www.w3.org/2001/XMLSchema-instance' as "xsi"
),
'Body/Flight'
passing xmltype(
'<?xml version="1.0" encoding="utf-16"?>
<Body xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://some/name.space">
<Sender>
<System>ConnectionManagement</System>
</Sender>
<Flight>
<Airline>ABC</Airline>
<Number>1234</Number>
<SubList>
<SubListItem xmlns="">
<Gate>X</Gate>
</SubListItem>
<SubListItem xmlns="">
<Gate>Y</Gate>
</SubListItem>
<SubListItem xmlns="">
<Gate>Z</Gate>
</SubListItem>
</SubList>
</Flight>
</Body>'
)
columns airline varchar2(100) path 'Airline'
, flightNumber VARCHAR2(5) path 'Number'
, subList XMLTYPE path 'SubList'
) f
, xmltable (
xmlnamespaces( default 'http://some/name.space'),
'/SubList/SubListItem'
passing f.subList
columns gate varchar2(5) path 'Gate'
) fl
;
How can I target the Gate node when the XML looks like this?

Leave the default namespace alone in the second XMLTable, and specify a named namespace for the path you do have:
...
, xmltable (
xmlnamespaces( 'http://some/name.space' as "ns"),
'/ns:SubList/SubListItem'
passing f.subList
columns gate varchar2(5) path 'Gate'
) fl
;
AIRLINE FLIGH GATE
---------- ----- -----
ABC 1234 X
ABC 1234 Y
ABC 1234 Z
The SubList still has to match that, but as the child nodes don't the default is incorrect the way you have it. If you remove the xmlns="" as you mentioned in the question then that inherits the namespace from its parent, so your default works. With that override to no-namespace you can't use a default.

Related

Parsing XML with XMLTable n Oracle

I have one xml which holds customer and order details. I am parsing it using XMLTable. I think I am giving the correct XPath but I am getting no output.
Here is what I have tried. Hoping for assistance.
**DECLARE
l_raw_xml CLOB:= '<?xml version="1.0" encoding="utf-8"?>
<Root
xmlns="http://www.adventure-works.com">
<Customers>
<Customer CustomerID="GREAL">
<CompanyName>Great Lakes Food Market</CompanyName>
<ContactName>Howard Snyder</ContactName>
<ContactTitle>Marketing Manager</ContactTitle>
<Phone>(503) 555-7555</Phone>
<FullAddress>
<Address>2732 Baker Blvd.</Address>
<City>Eugene</City>
<Region>OR</Region>
<PostalCode>97403</PostalCode>
<Country>USA</Country>
</FullAddress>
</Customer>
</Customers>
<Orders>
<Order>
<CustomerID>LETSS</CustomerID>
<EmployeeID>6</EmployeeID>
<OrderDate>1997-11-10T00:00:00</OrderDate>
<RequiredDate>1997-12-08T00:00:00</RequiredDate>
<ShipInfo ShippedDate="1997-11-21T00:00:00">
<ShipVia>2</ShipVia>
<Freight>45.97</Freight>
<ShipName>Let Stop N Shop</ShipName>
<ShipAddress>87 Polk St. Suite 5</ShipAddress>
<ShipCity>San Francisco</ShipCity>
<ShipRegion>CA</ShipRegion>
<ShipPostalCode>94117</ShipPostalCode>
<ShipCountry>USA</ShipCountry>
</ShipInfo>
</Order>
</Orders>
</Root>';
l_xml_type XMLTYPE:=XMLTYPE.createXML(l_raw_xml);
cursor c_make_xml_object
is
SELECT x.*,
y.*
FROM XMLTABLE('/Root/Customers/Customer'
PASSING l_xml_type
COLUMNS
CustomerID VARCHAR2(255) PATH '#CustomerID',
CompanyName VARCHAR2(255) PATH 'CompanyName',
FullAddress XMLTYPE PATH 'FullAddress') x,
XMLTABLE('/FullAddress'
PASSING x.FullAddress
COLUMNS
Address VARCHAR2(255) PATH 'Address',
City VARCHAR2(255) PATH 'City') y ;
BEGIN
FOR rec IN c_make_xml_object
loop
DBMS_OUTPUT.PUT_LINE(rec.CustomerID);
DBMS_OUTPUT.PUT_LINE(rec.CompanyName);
END LOOP;
END;
/**
Do I have to account for the namespace? I had added namespace code but it did not work/
Change to this to include a default namespace:
FROM XMLTABLE(xmlnamespaces(default 'http://www.adventure-works.com'),
'/Root/Customers/Customer' ....
....
XMLTABLE(xmlnamespaces(default 'http://www.adventure-works.com'), '/FullAddress'
.....
Yes, a proper namespace definition is needed within the SQL query such as below :
SELECT x.*, y.*
FROM XML_TAB t,
XMLTABLE(XMLnamespaces('http://www.adventure-works.com' as "ns"),
'ns:Root/ns:Customers/ns:Customer'
PASSING t.xml_data
COLUMNS
CustomerID VARCHAR2(255) PATH '#CustomerID',
CompanyName VARCHAR2(255) PATH 'ns:CompanyName',
FullAddress XMLTYPE PATH 'ns:FullAddress'
) x,
XMLTABLE(XMLnamespaces('http://www.adventure-works.com' as "ns"),
'ns:FullAddress'
PASSING x.FullAddress
COLUMNS
Address VARCHAR2(255) PATH 'ns:Address',
City VARCHAR2(255) PATH 'ns:City') y ;
Demo with SQL
Demo with PL/SQL

Extracting values from XML column in Oracle

I have some data in an Oracle table which is stored in an XML-format string (column response in table WS_LOG).
I would like to extract data from each different node below <MedicalProcedureOutput>, but I'm having some difficulties in getting to each of the nodes. Can you spot what I'm doing wrong?
Here's what I'm trying (here trying to retrieve the value for the icpcID tag):
SELECT a.id,
t1.icpcID FROM
GH.WS_LOG a,
xmltable(
xmlnamespaces(
'http://schemas.xmlsoap.org/soap/envelope/' as "soap",
'http://www.w3.org/2001/XMLSchema' as "xsd",
'http://www.w3.org/2001/XMLSchema-instance' as "xsi"),
'/soap:Envelope/soap:Body/createBillingSubmissionForAFEBSGResponse/createBillingSubmissionForAFEBSGResult/proceduresList/MedicalProcedureOutput' passing xmltype(a.response) columns
icpcID varchar2(50) path 'ipcdId') t1
And here's some example data
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Body>
<createBillingSubmissionForAFEBSGResponse xmlns="urn:bcpcorp.net/ws/bsgFacets/FacetsBillingService">
<createBillingSubmissionForAFEBSGResult>
<status>0</status>
<outputMessage />
<statusElement />
<claimID>18E002021300</claimID>
<claimStatus>01</claimStatus>
<claimStatusReason>AGFP</claimStatusReason>
<totalChargeValue>35.0000</totalChargeValue>
<totalPayableValue>0.0000</totalPayableValue>
<paitentPaidvalue>17.5</paitentPaidvalue>
<totalDebitAmount>0</totalDebitAmount>
<proceduresList>
<MedicalProcedureOutput>
<ipcdId>011801</ipcdId>
<otherdisallowedAmountResponsibility>N</otherdisallowedAmountResponsibility>
</MedicalProcedureOutput>
</proceduresList>
</createBillingSubmissionForAFEBSGResult>
</createBillingSubmissionForAFEBSGResponse>
</soap:Body>
</soap:Envelope>
I would expect to be getting the value '011801'. Please note that multiple <MedicalProcedureOutput> nodes may occur, and they would be organized as follows:
<MedicalProcedureOutput>
<ipcdId>725013</ipcdId>
<otherdisallowedAmountResponsibility>N</otherdisallowedAmountResponsibility>
</MedicalProcedureOutput>
<MedicalProcedureOutput>
<ipcdId>725105</ipcdId>
<otherdisallowedAmountResponsibility>N</otherdisallowedAmountResponsibility>
</MedicalProcedureOutput>
You need to declare a default namespace for everything from within the body, since createBillingSubmissionForAFEBSGResponse has its own xmlns unnamed (therefore default) declaration which applies from that node onwards; so:
SELECT a.id,
t1.icpcID FROM
GH.WS_LOG a,
xmltable(
xmlnamespaces(
default 'urn:bcpcorp.net/ws/bsgFacets/FacetsBillingService',
'http://schemas.xmlsoap.org/soap/envelope/' as "soap",
'http://www.w3.org/2001/XMLSchema' as "xsd",
'http://www.w3.org/2001/XMLSchema-instance' as "xsi"
),
'/soap:Envelope/soap:Body/createBillingSubmissionForAFEBSGResponse/createBillingSubmissionForAFEBSGResult/proceduresList/MedicalProcedureOutput' passing xmltype(a.response) columns
icpcID varchar2(50) path 'ipcdId') t1
/
ID ICPCID
---------- --------------------------------------------------
1 011801
or with multiple nodes as you showed later int he question this will return:
ID ICPCID
---------- --------------------------------------------------
2 725013
2 725105
db<>fiddle
You should take a look into to solve it with the EXTRACTVALUE function, here You can find some information about it.

Reading XML Namespace using Oracle SQL

My XML Looks like below
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<wfm:Statement xmlns:wfm="http://example.org/sample/xsd/sampleStatement/2013/05" xmlns:wfmMerchant="http://www.eds.com/sample/xsd/wfmMerchant/2012/03"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<wfm:StatementParameters>
<wfmMerchant:HierarchyCd>012-12-002-107-050</wfmMerchant:HierarchyCd>
</wfm:StatementParameters>
<StatementAmount>27.140</StatementAmount>
</wfm:Statement>
I am trying to get the value of StatementAmount tag using Oracle query like below
select MS.MERCHANT,MS.CHAIN_HIERARCHY_CD,MS.CYCLE_DATE, X.StatementAmount
FROM CHAIN_STATMNT_HIST_XML MS
CROSS JOIN XMLTABLE(XMLNAMESPACES('http://example.org/sample/xsd/sampleStatement/2013/05' AS "wfm", 'http://www.eds.com/sample/xsd/wfmMerchant/2012/03' as wfmmerchant
default 'http://www.w3.org/2001/XMLSchema-instance')
,'/wfm:Statement/StatementAmount' passing xmltype(MS.XML_REPORT)
columns StatementAmount varchar(18) path '.')X
But, I am always getting NULL. I can able to successfully retrieve Hierarchy value from the XML which has namespace. But StatementAmount tag doesn't have any namespace and I have trouble retrieving it.
Can someone help with this issue ?
Your default namespace declaration seems to be causing the problem; without that (and ignoring wfmMerchant):
-- CTE for sample data
with CHAIN_STATMNT_HIST_XML (merchant, chain_hierarchy_cd, cycle_date, XML_REPORT) as (
select 1, 2, sysdate, '<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<wfm:Statement xmlns:wfm="http://example.org/sample/xsd/sampleStatement/2013/05" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<wfm:StatementParameters>
<!-- excluding this as namespace not provided -->
<!-- <wfmMerchant:HierarchyCd>012-12-002-107-050</wfmMerchant:HierarchyCd> -->
</wfm:StatementParameters>
<StatementAmount>27.140</StatementAmount>
</wfm:Statement>' from dual
)
-- actual query
select MS.MERCHANT,MS.CHAIN_HIERARCHY_CD,MS.CYCLE_DATE, X.StatementAmount
FROM CHAIN_STATMNT_HIST_XML MS
CROSS JOIN XMLTABLE(
XMLNAMESPACES('http://example.org/sample/xsd/sampleStatement/2013/05' AS "wfm"),
'/wfm:Statement/StatementAmount' passing xmltype(MS.XML_REPORT)
columns StatementAmount varchar(18) path '.'
) X
/
MERCHANT CHAIN_HIERARCHY_CD CYCLE_DATE STATEMENTAMOUNT
---------- ------------------ ---------- ------------------
1 2 2018-09-04 27.140
I'm not sure why you would use varchar2(18) as the datatype rather than number; and if there is only one statement amount per statement you could do:
select MS.MERCHANT,MS.CHAIN_HIERARCHY_CD,MS.CYCLE_DATE, X.StatementAmount
FROM CHAIN_STATMNT_HIST_XML MS
CROSS JOIN XMLTABLE(
XMLNAMESPACES('http://example.org/sample/xsd/sampleStatement/2013/05' AS "wfm"),
'/wfm:Statement' passing xmltype(MS.XML_REPORT)
columns StatementAmount number path 'StatementAmount'
) X

How to pull XML key "value" from SQL CLOB

I am attempting to extract information from XML stored in a CLOB column. I've searched the forums and thus far have been unable to get the data to pull as needed. I have a basic understanding of SQL but this is beyond me.
The XML is similar to the following:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Header>
<OrderNum value="12354321"/>
<ExtractDate value="11-30-2012"/>
<RType value="Status"/>
<Company value="Company"/>
</Header>
<Body>
<Status>
<Order>
<ActivityType value="ValidateRequest"/>
<EndUser>
<Name value="Schmo, Joe"/>
<Address>
<SANO value="12345"/>
<SASN value="Mickey Mouse"/>
<SATH value="Lane"/>
<SASS value="N"/>
<City value="Orlando"/>
<State value="FL"/>
<Zip value="34786"/>
<Number value="5550000"/>
</Address>
</EndUser>
<COS value="1"/>
<TOS value="3"/>
<MainNumber value="5550000"/>
</Order>
<ErrorCode value="400"/>
<ErrorMessage value="RECEIVED"/>
</Status>
</Body>
</Response>
I want to get the values under "Address".
I've tried the following but it returns "NULL".
SELECT EXTRACTVALUE(XMLTYPE(RESPONSE_CLOB),'/Response/Body/Status/Order/EndUser/Address/SANO') AS SANO
FROM RESPONSE_TABLE
WHERE ROWNUM < 2
I am trying to get it so I can pull the "12345" assigned as "value" in "SANO" (ultimately getting the value for other fields, but want to at least get the one working first).
You're currently retrieving the text value of the node, but 12345 is the value attribute of the element rather than its text content. So you would need to use the #attribute syntax, i.e.:
SELECT EXTRACTVALUE(XMLTYPE(RESPONSE_CLOB),'/Response/Body/Status/Order/EndUser/Address/SANO/#value') AS SANO
FROM RESPONSE_TABLE
WHERE ROWNUM < 2;
SANO
--------------------
12345
But extractvalue is deprecated; assuming you're on a recent version of Oracle it would be better to use an XMLQuery:
SELECT XMLQUERY(
'/Response/Body/Status/Order/EndUser/Address/SANO/#value'
PASSING XMLTYPE(RESPONSE_CLOB)
RETURNING CONTENT
) AS SANO
FROM RESPONSE_TABLE
WHERE ROWNUM < 2;
You may find it even easier to use an XMLTable - necessary if an XML document has multiple Address nodes, but even with just one pulling the values out as columns is less repetitive, and it makes it easier to retrieve suitable data types:
select x.*
from response_table rt
cross join xmltable(
'/Response/Body/Status/Order/EndUser/Address'
passing xmltype(rt.response_clob)
columns sano number path 'SANO/#value',
sasn varchar2(30) path 'SASN/#value',
sath varchar2(10) path 'SATH/#value'
-- etc.
) x
where rownum < 2;
SANO SASN SATH
-------------------- ------------------------------ ----------
12345 Mickey Mouse Lane
Read more about using these functions to query XML data.

Check length of column in XMLTable

This is my XML in an XMLTYPE column:
<customers>
<customer>
<name>abc</name>
<surname>abc</surname>
<address>abc</address>
</customer>
<customer>
<name>abc</name>
<surname>abc</surname>
<address>abc</address>
</customer>
</customers>
I want that if the length of a field exceeds the allowed length, 15 charcaters, then use a default value instead.
In the guide for DB2 I found this example:
select ...from .., XMLTABLE
('$INFO/customerinfo*' passing
columns
city varchar(16) path a'addr/city(if(string-lenght(.)<=16) then . else "Error!")'
) ..
How can I do it in Oracle? This is my current XMLTABLE attempt:
select ..XMLTABLE
('customers/*' passing ..
columns
...
"address" varchar(15) path 'indirizzo/(if(lenght(.)<=15) then . else "Error!")'
) data;
But that gets:
ORA-19237. unable to resolve call to function -fn:length
You have typos and inconsistent naming so it's hard to tell exactly what you're really doing to get that error, but it works if you use the string-length() function from your DB2 example, rather than length() (or lenght()):
"address" varchar2(15) path 'address/(if(string-length(.)<=15) then . else "Error!")'
With modified data to trigger the error behaviour:
select *
from XMLTABLE ('customers/*'
passing xmltype('<customers>
<customer>
<name>abc</name>
<surname>abc</surname>
<address>abc def ghi jkl mno</address>
</customer>
<customer>
<name>abc</name>
<surname>abc</surname>
<address>abc</address>
</customer>
</customers>')
columns
"address" varchar(15) path 'address/(if(string-length(.)<=15) then . else "Error!")'
) data;
address
---------------
Error!
abc