Search code examples
xmloracle-databasexmltypexmltable

From XML to list of paths in Oracle 12c


From XML to list of paths in Oracle PL/SQL environment shows how to list all the XPATHs of each XML element in an XML document and their corresponding values. My Question is how to list all the XPATHs of the XML elements and the XPATHs of their attributes and the corresponding values?

Oracle Setup:

CREATE TABLE xml_data ( xml ) AS
  SELECT XMLTYPE('
    <ALFA>
      <BETA>0123</BETA> 
      <GAMMA attribute1="value1">2345</GAMMA> 
      <DELTA attribute2="value2"> 
         <EPSILON attribute3="value3" attribute4="value4">3</EPSILON> 
      </DELTA> 
    </ALFA> 
  ')
  FROM DUAL;

Expected Output:

| ELEMENT_PATH                   | ELEMENT_TEXT |
|--------------------------------|--------------|
| ALFA                           | (null)       |
| ALFA/BETA                      | 0123         |
| ALFA/GAMMA                     | 2345         |
| ALFA/GAMMA/@attribute1         | value1       |
| ALFA/DELTA                     | (null)       |
| ALFA/DELTA/@attribute2         | value2       |
| ALFA/DELTA/EPSILON             | 3            |
| ALFA/DELTA/EPSILON/@attribute3 | value3       |
| ALFA/DELTA/EPSILON/@attribute4 | value4       | 

Solution

  • SQL Fiddle

    Oracle 11g R2 Schema Setup:

    CREATE TABLE xml_data ( xml ) AS
      SELECT XMLTYPE('
        <ALFA>
          <BETA>0123</BETA> 
          <GAMMA attribute1="value1">2345</GAMMA> 
          <DELTA attribute2="value2"> 
             <EPSILON attribute3="value3" attribute4="value4">3</EPSILON> 
          </DELTA> 
        </ALFA> 
      ')
      FROM DUAL;   
    

    Query 1:

    select xpath, text
    from   xml_data d
           CROSS JOIN
           XMLTable( 
             'for $i in $doc/descendant-or-self::*
                let $path := $i/string-join(ancestor-or-self::*/name(.), ''/'')
                return <data>{attribute path {$path}, attribute value {$i/text()}}</data>' 
             PASSING d.xml AS "doc"
             COLUMNS xpath varchar2(4000) path '/data/@path', 
                     text  varchar2(4000) path '/data/@value'
           )
    UNION ALL
    select xpath, text
    from   xml_data d
           CROSS JOIN
           XMLTable( 
             'for $i in $doc/descendant-or-self::*
                let $path := $i/string-join(ancestor-or-self::*/name(.), ''/'')
                for $j in $i/attribute::*
                  return <data>{attribute path { concat( $path, "/@", $j/name(.) ) }, attribute value {$j}}</data>' 
             PASSING d.xml AS "doc"
             COLUMNS xpath varchar2(4000) path '/data/@path', 
                     text  varchar2(4000) path '/data/@value'
           )
    

    Results:

    |                          XPATH |   TEXT |
    |--------------------------------|--------|
    |                           ALFA | (null) |
    |                      ALFA/BETA |   0123 |
    |                     ALFA/GAMMA |   2345 |
    |                     ALFA/DELTA | (null) |
    |             ALFA/DELTA/EPSILON |      3 |
    |         ALFA/GAMMA/@attribute1 | value1 |
    |         ALFA/DELTA/@attribute2 | value2 |
    | ALFA/DELTA/EPSILON/@attribute3 | value3 |
    | ALFA/DELTA/EPSILON/@attribute4 | value4 |