Skip to Main Content
  • Questions
  • Problem reading data from a flexible attribute based XML

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, AC.

Asked: September 08, 2018 - 1:57 am UTC

Last updated: September 11, 2018 - 2:40 pm UTC

Version: 11gR2

Viewed 1000+ times

You Asked

Hi Tom,
I am not an XML expert. I have a flexible attribute based XML stored into a table in XMLTYPE column TBTest.C1(XMLTYPE) which is a message from upstream queue. The XPATHs of message are like -
/*[name()='Message']
/*[name()='Message']/*[name()='Version']
/*[name()='Message']/*[name()='DateTime']
/*[name()='Message']/*[name()='Object']
/*[name()='Message']/*[name()='ObjectProperties']
/*[name()='Message']/*[name()='ObjectProperties']/*[name()='EventId']
/*[name()='Message']/*[name()='ObjectProperties']/*[name()='SourceSystem']
/*[name()='Message']/*[name()='ObjectProperties']/*[name()='EventType']
/*[name()='Message']/*[name()='AdditionalObjectProperties']
/*[name()='Message']/*[name()='AdditionalObjectProperties']/*[name()='ObjectElementValue']
/*[name()='Message']/*[name()='TestStatus']
/*[name()='Message']/*[name()='TestStatus']/*[name()='TestResult']


Sample data:
<Message xmlns="urn:message" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="urn:message message.xsd">
<Version>v2.0</Version>
<DateTime>2018-06-10T11:00:05Z</DateTime>
<Object>US Event</Object>
<ObjectProperties>
   <EventId>0000121</EventId>
   <SourceSystem>S1</SourceSystem>
   <EventType>New</EventType>
</ObjectProperties>
<AdditionalObjectProperties type="GridTestCase1">
   <ObjectElementValue name="TestGrid">42440</ObjectElementValue>
   <ObjectElementValue name="AssetId">B1-42440</ObjectElementValue>
   <ObjectElementValue name="GridType">B1</ObjectElementValue>
</AdditionalObjectProperties>
<AdditionalObjectProperties type="GridTestResult">
   <ObjectElementValue name="EventBucket">New</ObjectElementValue>
</AdditionalObjectProperties>
<TestStatus ControlId="T1">
   <TestResult TestId="BB-0001" ResultId="1121">Pass</TestResult>
   <TestResult TestId="BB-0002" ResultId="1122">Fail</TestResult>
   <TestResult TestId="BB-0003" ResultId="1123">NA</TestResult>
</TestStatus>
<TestStatus ControlId="T2">
   <TestResult TestId="BT-0001" ResultId="1501">Fail</TestResult>
   <TestResult TestId="BT-0002" ResultId="1502">Fail</TestResult>
   <TestResult TestId="BT-0003" ResultId="1503">Fail</TestResult>
</TestStatus>
</Message>


I wish to extract this data into tabular form. I have been trying to use the XMLTABLE operator to get the data but couldn't succeed.
How can I achieve this, any quick SQL to solve this?

Thanks!

and Chris said...

You can use XMLTable to do this. Here's few principles:

The XML has the namespace xmlns="urn:message". So you'll want to include:

xmlnamespaces ( default 'urn:message' )


At the start of all your XMLTable calls. So to get the values for elements, do:

select tests.*
from   t, xmltable (
  xmlnamespaces ( default 'urn:message' ),
  '/' passing t.x
  columns 
    DateTime path '/Message/DateTime',
    event_id path '/Message/ObjectProperties/EventId'
  ) tests;

DATETIME               EVENT_ID   
2018-06-10T11:00:05Z   0000121 


You have multiple elements with the same name. To extract these into separate rows, chain together XMLTable calls. Do this by defining the repeated element as an XMLType column. Then to turn repeated elements into rows, use the double slash prefix in the passing clause of the next XMLTable call:

select event_id, datetime, control_id
from   t, xmltable (
  xmlnamespaces ( default 'urn:message' ),
  '/' passing t.x
  columns 
    DateTime path '/Message/DateTime',
    event_id path '/Message/ObjectProperties/EventId',
    test_status xmltype path '/Message/TestStatus'
  ) tests, xmltable (
    xmlnamespaces ( default 'urn:message' ),
    '//TestStatus' passing tests.test_status
    columns 
      control_id  path '@ControlId'
  ) results;

EVENT_ID   DATETIME               CONTROL_ID   
0000121    2018-06-10T11:00:05Z   T1           
0000121    2018-06-10T11:00:05Z   T2 


The @ prefix allows you access attribute values.

From here, keep chaining the calls as needed:

select event_id, datetime, control_id, test_id, res
from   t, xmltable (
  xmlnamespaces ( default 'urn:message' ),
  '/' passing t.x
  columns 
    DateTime path '/Message/DateTime',
    event_id path '/Message/ObjectProperties/EventId',
      test_status xmltype path '/Message/TestStatus'
  ) tests, xmltable (
    xmlnamespaces ( default 'urn:message' ),
    '//TestStatus' passing tests.test_status
    columns 
      test_result xmltype path '/TestStatus/TestResult',
      control_id  path '@ControlId'
  ) results, xmltable (
    xmlnamespaces ( default 'urn:message' ),
    '//TestResult' passing results.test_result
    columns 
      test_id path '@TestId',
      res path 'text()'
  );

EVENT_ID   DATETIME               CONTROL_ID   TEST_ID   RES    
0000121    2018-06-10T11:00:05Z   T1           BB-0001   Pass   
0000121    2018-06-10T11:00:05Z   T1           BB-0002   Fail   
0000121    2018-06-10T11:00:05Z   T1           BB-0003   NA     
0000121    2018-06-10T11:00:05Z   T2           BT-0001   Fail   
0000121    2018-06-10T11:00:05Z   T2           BT-0002   Fail   
0000121    2018-06-10T11:00:05Z   T2           BT-0003   Fail  



Table create and populate script:

create table t (
  x xmltype
);

insert into t values (xmltype ('<Message xmlns="urn:message" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="urn:message message.xsd">
<Version>v2.0</Version>
<DateTime>2018-06-10T11:00:05Z</DateTime>
<Object>US Event</Object>
<ObjectProperties>
   <EventId>0000121</EventId>
   <SourceSystem>S1</SourceSystem>
   <EventType>New</EventType>
</ObjectProperties>
<AdditionalObjectProperties type="GridTestCase1">
   <ObjectElementValue name="TestGrid">42440</ObjectElementValue>
   <ObjectElementValue name="AssetId">B1-42440</ObjectElementValue>
   <ObjectElementValue name="GridType">B1</ObjectElementValue>
</AdditionalObjectProperties>
<AdditionalObjectProperties type="GridTestResult">
   <ObjectElementValue name="EventBucket">New</ObjectElementValue>
</AdditionalObjectProperties>
<TestStatus ControlId="T1">
   <TestResult TestId="BB-0001" ResultId="1121">Pass</TestResult>
   <TestResult TestId="BB-0002" ResultId="1122">Fail</TestResult>
   <TestResult TestId="BB-0003" ResultId="1123">NA</TestResult>
</TestStatus>
<TestStatus ControlId="T2">
   <TestResult TestId="BT-0001" ResultId="1501">Fail</TestResult>
   <TestResult TestId="BT-0002" ResultId="1502">Fail</TestResult>
   <TestResult TestId="BT-0003" ResultId="1503">Fail</TestResult>
</TestStatus>
</Message>'));


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

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library