0

I am currently trying to extract value from the clob column but always getting the result as null.

I tried out many possible scenarios but for me it is always returning as null.

Attached is the actual xml

<TenderOffer xmlns="http://xmlns.oracle.com/apps/otm">
    <Shipment>
        <ShipmentHeader/>
        <SEquipment/>
        <ShipmentStop/>
        <ShipmentStop/>
        <Location/>
        <Location/>
        <Release/>
        <RATE_OFFERING>
            <RATE_OFFERING_ROW>
                <USER_CLASSIFICATION3>ZXF</USER_CLASSIFICATION3>
            </RATE_OFFERING_ROW>
        </RATE_OFFERING>
    </Shipment>
    </TenderOffer>

and below is the actual query,

select 
        itc.element_name,
        extractvalue(XMLTYPE(XML_BLOB), '/TenderOffer/Shipment/RATE_OFFERING/RATE_OFFERING_ROW/USER_CLASSIFICATION3/text()'),
    XMLTYPE(XML_BLOB)
        from i_transaction itc
        where itc.i_transaction_no = 31553115
            and rownum = 1
17
  • Verify your code for typo, I get abxy with your query: SELECT EXTRACTVALUE(XMLTYPE('<Header> <SecondHeader> <ThirdHeader> <FourthHeader> <Value1>abcd</Value1> <Value2>xyz</Value2> <Value3>abxy</Value3> </FourthHeader> </ThirdHeader> </SecondHeader> </Header>'), '/Header/SecondHeader/ThirdHeader/FourthHeader/Value3/text()') FROM dual; Commented Jun 13, 2017 at 8:51
  • Hi Mate, i updated the xml and actual query, can you please have a look.Thanks. Commented Jun 13, 2017 at 9:07
  • Posting the XML as an image ins't terribly useful, though from that image there aren't any obvious typos. (Why is your column called xml_blob if it's a CLOB?). Presumably your query shows the CLOB value you expect, with just the extracted value as null? Can you cut it down to a simpler example that actually reproduces the problem, since your first 'Header' version doesn't? Commented Jun 13, 2017 at 9:31
  • 1
    Cut it down to a minimal example that still shows the problem - remove any nodes that aren't relevant/in the hierarchy, and hide any data you don't want to share. But check that when you run that cut-down version you do still see the same issue. Commented Jun 13, 2017 at 9:34
  • 1
    Yes, that was rather an important omission... even from what you said was your 'actual XML'. Guess I should have asked. *8-) Commented Jun 13, 2017 at 13:34

2 Answers 2

4

Your updated XML has a namespace, which finally reveals the issue. You need to specify the namespace as part of the XML extraction, which is simpler with the XMLTable approach; in this case you can just treat it as the default namespace:

select itc.element_name, x.user_classification3
from i_transaction itc
cross join xmltable(
  xmlnamespaces(default 'http://xmlns.oracle.com/apps/otm'),
    '/TenderOffer/Shipment/RATE_OFFERING/RATE_OFFERING_ROW'
  passing xmltype(itc.xml_blob)
  columns user_classification3 varchar2(10) path 'USER_CLASSIFICATION3'
) x
where itc.i_transaction_no = 31553115
and rownum = 1;

ELEMENT_NA USER_CLASS
---------- ----------
dummy      ZXF       

or with XMLQuery:

select itc.element_name, xmlquery(
  'declare default element namespace "http://xmlns.oracle.com/apps/otm"; (: :)
    /TenderOffer/Shipment/RATE_OFFERING/RATE_OFFERING_ROW/USER_CLASSIFICATION3/text()'
  passing xmltype(itc.xml_blob)
  returning content
) x
from i_transaction itc
where itc.i_transaction_no = 31553115
and rownum = 1;

ELEMENT_NA X                                                                               
---------- --------------------------------------------------------------------------------
dummy      ZXF                                                                             

If you wanted to keep using the deprecated extractvalue() function you can supply the namespace as an argument to that too, again as shown in the documentation:

select itc.element_name,
  extractvalue(xmltype(xml_blob),
    '/TenderOffer/Shipment/RATE_OFFERING/RATE_OFFERING_ROW/USER_CLASSIFICATION3/text()',
    'xmlns="http://xmlns.oracle.com/apps/otm"')
from i_transaction itc where itc.i_transaction_no = 31553115 and rownum = 1;
Sign up to request clarification or add additional context in comments.

Comments

0

In your code you use XML_BLOB as the variable - so I assumed a BLOB data type, which needs converting to a CLOB. However, if you have a CLOB value then you can skip using the BLOB_TO_CLOB function:

Oracle 11 Setup:

CREATE OR REPLACE FUNCTION BLOB_TO_CLOB( b BLOB )
RETURN CLOB 
IS
  c            CLOB;
  n            INTEGER     := 1;
  w   CONSTANT PLS_INTEGER := 32767;
  len CONSTANT INTEGER     := LENGTH( b );
BEGIN
  IF b IS NULL THEN
    RETURN NULL;
  END IF;

  IF len = 0 THEN
    RETURN EMPTY_CLOB();
  END IF;

  DBMS_LOB.CREATETEMPORARY( c, TRUE );
  WHILE ( n + w <= len ) LOOP
    DBMS_LOB.WRITEAPPEND( c, w, UTL_RAW.CAST_TO_VARCHAR2( DBMS_LOB.SUBSTR( b, w, n ) ) );
    n := n + w;
  END LOOP;
  DBMS_LOB.WRITEAPPEND( c, len - n + 1, UTL_RAW.CAST_TO_VARCHAR2( DBMS_LOB.SUBSTR( b, len - n + 1, n ) ) );
  RETURN c;
END;
/

CREATE TABLE blob_test ( value BLOB );

INSERT INTO blob_test VALUES (
  UTL_RAW.CAST_TO_RAW(
    '<Header>' ||
      '<SecondHeader>' ||
        '<ThirdHeader>' ||
          '<FourthHeader>' ||
            '<Value1>abcd</Value1>' ||
            '<Value2>xyz</Value2>' ||
            '<Value3>abxy</Value3>' ||
          '</FourthHeader>' ||
        '</ThirdHeader>' ||
      '</SecondHeader>' ||
    '</Header>'
  )
);

Query:

EXTRACT and EXTRACTVALUE are deprecated - use XMLTABLE instead:

SELECT Value1, Value2, Value3
FROM   blob_test b
       CROSS JOIN
       XMLTABLE(
         '/Header/SecondHeader/ThirdHeader/FourthHeader'
         PASSING XMLTYPE( BLOB_TO_CLOB( b.value ) )
         COLUMNS Value1 VARCHAR2(50) PATH 'Value1/text()',
                 Value2 VARCHAR2(50) PATH 'Value2/text()',
                 Value3 VARCHAR2(50) PATH 'Value3/text()'
       ) x;

Output:

VALUE1 VALUE2 VALUE3
------ ------ ------
abcd   xyz    abxy

Query 2:

If you do want to use EXTRACTVALUE you can remove the /text() from the XPath as EXTRACTVALUE will do that for you (but it should still work with it there too) and convert the BLOB to a CLOB:

SELECT EXTRACTVALUE(
         XMLTYPE( BLOB_TO_CLOB( value ) ), 
         '/Header/SecondHeader/ThirdHeader/FourthHeader/Value3'
       )
FROM   blob_test

Query - Update:

Simplification from @AlexPoole. The call to BLOB_TO_CLOB is not necessary as the XMLTYPE constructor can take a BLOB and a character set id:

SELECT Value1, Value2, Value3
FROM   blob_test b
       CROSS JOIN
       XMLTABLE(
         '/Header/SecondHeader/ThirdHeader/FourthHeader'
         PASSING XMLTYPE( b.value, NLS_CHARSET_ID('UTF8') )
         COLUMNS Value1 VARCHAR2(50) PATH 'Value1/text()',
                 Value2 VARCHAR2(50) PATH 'Value2/text()',
                 Value3 VARCHAR2(50) PATH 'Value3/text()'
       ) x;

3 Comments

Bit of a side issue, as the OP indicated the column is a CLOB despite the name (!), but you don't have to convert the BLOB first anyway as long as you know the character set to pass to the XMLType constructor - e.g. xmltype(xml_blob, nls_charset_id('UTF8')).
@AlexPoole In the first paragraph I did address that discrepancy and opted to address the more complicated of the two possible versions and added the note that for a CLOB you can just remove the call to BLOB_TO_CLOB. However, it is good to know the additional argument for XMLType.
Yes, it's just an alternative if it is a BLOB - I was just adding that the more complicated scenario still doesn't necessarily need the blob_to_clob function, which incidentally uses the DB character set. It's a useful one to have around anyway of course.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.