Skip to Main Content
  • Questions
  • XMLTYPE returning unknown special character

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Amit.

Asked: October 16, 2025 - 1:11 am UTC

Last updated: November 14, 2025 - 5:07 pm UTC

Version: 19.16.0.0.0

Viewed 1000+ times

You Asked

Hello Sir,

We're using Oracle Database 19C Enterprise edition.
We're getting a XML tag in Varchar2 format. Then we're using XMLTYPE to convert that to XML. However, incase when there's special character in the XML tag it's giving a unknown special character in the output. What can we do to get the same output

Sample SQL:
select XMLTYPE('<tag1> a''bc </tag1>') from dual;


Output:
<tag1> a&apos;bc </tag1>


Expected output:
<tag1> a'bc </tag1>

and Chris said...

You can use XMLSerialize/XMLCast/XMLTable to convert the value to a string before returning it to the client:

select x
, xmlserialize ( content t.x as varchar2(20) ) xserialized
, xmlcast ( xmlquery ( '/tag1/text()' passing t.x returning content ) as varchar2(20) ) xcast
from (
  select xmltype ( '<tag1> a''bc </tag1>' ) x
  from   dual
) t;

X                              XSERIALIZED                    XCAST
------------------------------ ------------------------------ --------------------
<tag1> a&apos;bc </tag1>       <tag1> a'bc </tag1>             a'bc

select *
from   xmltable ( 
  '/' passing xmltype ( '<tag1> a''bc </tag1>' ) 
  columns 
    tag1 varchar2(20) path '/tag1'
) t;

TAG1
--------------------
 a'bc

Rating

  (4 ratings)

Comments

A few questions for clarification

mathguy, October 24, 2025 - 3:19 am UTC

Amit - if you don't mind, a few questions for clarification.

First, what do you mean by "unknown special character"? &apos; is not an unknown character. It is an XML entity ("entity" is a technical XML term) representing "apostrophe".

Second, what do you mean by "the same output"? XMLTYPE returns a value of data type XMLTYPE. In XML, &apos; is exactly the same as a single apostrophe. Instead, I believe you are viewing the value on screen, as a string (the string representation of an XML tag) - and you want that to look like your input. That, as Chris demonstrated, is a matter of serializing your XMLTYPE value; but your value as you reported it is ALREADY the same as the input, as XMLTYPE values. (For comparison, 003 and 3 may not look the same on screen; but as NUMBERS, not as strings, they are the same!)

In any case, I am unable to reproduce what you reported. Using SQL Developer on a Windows 11 machine running Oracle 19.3, and running the query as a "script" (if I run it as a "query" the output will simply show <XMLTYPE> as the output, but then if I double-click on it I get the same output as when I run as "script"):

select banner_full from v$version;

BANNER_FULL
------------------------------------------------------------------------------------------
"Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production Version 19.3.0.0.0"





select XMLTYPE('<tag1> a''bc </tag1>') from dual;

XMLTYPE('<TAG1>A''BC</TAG1>')
-----------------------------
<tag1> a'bc </tag1>



I see the same output (with an actual apostrophe, not an XML entity representing it) on LiveSQL.Oracle.com. This seems to suggest that your observation may be rather dependent on your user interface. What IDE are you using to run your queries?

Amit Chaudhary, November 12, 2025 - 8:15 am UTC

DROP PUBLIC SYNONYM TABLE_A;
/
DROP TABLE TABLE_A;
/
CREATE TABLE TABLE_A (N_SEQ_NO NUMBER,D_SYSDATE DATE,D_SYSTIMESTAMP TIMESTAMP, V_VALUE_XML XMLTYPE,V_VALUE_STR VARCHAR2(4000));
/
CREATE PUBLIC SYNONYM TABLE_A FOR TABLE_A;
/
GRANT ALL ON TABLE_A TO RFORALLOBJECTS,SELECTONLY;
/
DECLARE
LV_INPUT VARCHAR2(32000):='<TAG1>A''BC</TAG1>'; ---This is the input
lv_xml XMLTYPE;

BEGIN
lv_xml := XMLTYPE(LV_INPUT);

INSERT INTO TABLE_A (N_SEQ_NO,D_SYSDATE,D_SYSTIMESTAMP, V_VALUE_XML, V_VALUE_STR)
VALUES(1,SYSDATE,SYSTIMESTAMP,LV_XML,LV_INPUT);

END;
/
SELECT * FROM TABLE_A;


above is the sample script. Can you please advise further :
1. how to use xmlserialize here ? Or, any alternative
2. The above single quote is just one sample, the input can also have & , < symbols.

Chris Saxon
November 13, 2025 - 2:42 pm UTC

You can use XMLCast:

DECLARE
  LV_INPUT VARCHAR2(32000):='<TAG1>A''BC</TAG1>'; ---This is the input
  lv_xml XMLTYPE;
BEGIN
  lv_xml := XMLTYPE(LV_INPUT);

  INSERT INTO TABLE_A (N_SEQ_NO,D_SYSDATE,D_SYSTIMESTAMP, V_VALUE_XML, V_VALUE_STR)
  VALUES(
    1,SYSDATE,SYSTIMESTAMP, lv_xml, LV_INPUT
  );

END;
/
select xmlcast ( v_value_xml as varchar2(100) )
from   table_a;


XMLCAST(V_VALUE_XMLASVARCHAR2(100))
--------------------------------------------------
A'BC

XMLType for special character

Amit Chaudhary, November 13, 2025 - 8:08 am UTC





https://freesql.com/?compressed_code=H4sIAAAAAAAACrWW3W%252BbMBDAn4vE%252F%252BCnhUy0NOmeGopkwF2R%252BJptomQvCBW3i0SgIyRN%252FvsJAqaQVV2aLJES3%252FnuZ%252Fv8cbdiBYjZ0yJlIHt6moiCiT0f%252BIFuWwYgc9dz5w6gULdRCJveSnyjNDCCFHXVQHJDgn6ErgfcwNERls2QzIlZGpY%252Fe5FaDiIUOj7gLRlMwym0AxTOHBvMHJvOfSQ3OkIxmEJsPEA8lr5dX18P5WnoQgcdaqFpYkRIr2NYLgIZNsRIFOxpaLl%252BQFubm3FpdHsHBuqDDQ3TxOz3mq0KTRRA56Nul4kZFdFBB%252Fi73QOLYpZ%252FZN3zNdarIluyHGcJ06CqdOQjUdnyJUp3RhYzTVXeSp%252Fi6HmUPv5qSbX8UTCUo6LRxM7Ps0e2Wh05UyuNF5tFvI6S4xz7HLR8SbIdyzWoG4OBie5VhavOAoZxnJfLm32Z%252F2zZjfa4RSufXbX6qfHegZDy0qSP5Unra86Bp7sXVh%252FkvuYc%252BFHLHZ0FOG6B47MAb1rgzWlAP1sVerbVVKVpnYyrd4Y3TwLS7DU1FsVOUxXePAlIiqgop7f%252FPwllZOu0yHfVI7hvHXlX%252F%252FHK8VfzvXeQG2iioCr9vDWYiEKyCbfLBFxcAHBRp9RJlf66qbNKfnvzaD%252B3wz4dfbdcUeDM27smSUtNOi3Nyq%252FlEoQpsFzqHZYFbT3QKwU6BUAn8Q9FoRKINJIb346nPa18SKATivl05JHMU78oEGQjg4pCPekrNKMYGlSqfQfd%252BDWR7W3Bm2eWP9hKwbaFNBwMr55ZsSryRfq8iRJpCDZhGi2Z%252FB%252BGrA%252FQuyPzIqjcC%252BqBesPldn9F4R57DjADaFfRCXyTl3IhrA4aQRTUB%252BWOE%252FgRbOusqrMWKhZyzYkoKE3IwVdQjdXWjZeXyDP%252BAEDKG6KBCgAA&code_language=PL_SQL&code_format=false





Hi,
I did a run again. However, we can't get the desired result. My PLSQL Code as below:
set define off;
DROP PUBLIC SYNONYM TABLE_A;
DROP TABLE TABLE_A;
CREATE TABLE TABLE_A (N_SEQ_NO NUMBER,D_SYSDATE DATE,D_SYSTIMESTAMP TIMESTAMP, V_VALUE_XML XMLTYPE,V_VALUE_STR VARCHAR2(4000),V_NAME VARCHAR2(4000),V_ADDRESS VARCHAR2(4000));
DECLARE
LV_INPUT VARCHAR2(32000):= '<HLACDDRequest>
<xmlData>
<xmlDataHeader>
<CustomerRole>A</CustomerRole>
<CompanyCode></CompanyCode>
<CompanyBranch></CompanyBranch>
</xmlDataHeader>
<xmlDataProcess>
<Individual>
<Employer>ABC''DEF</Employer>
<EmployerAddress>X&YZ</EmployerAddress>
</Individual>
<Address>
<AddressSequence></AddressSequence>
<AddressTypeCode></AddressTypeCode>
<Address1></Address1>
<Address2></Address2>
<Address3></Address3>
<PostBox></PostBox>
<PostCode></PostCode>
<TownCity></TownCity>
<State></State>
<Country></Country>
</Address>
</xmlDataProcess>
</xmlData>
</HLACDDRequest>
';
lv_xml XMLTYPE;
LV_NAME VARCHAR2(2000);
lv_address VARCHAR2(2000);
BEGIN

lv_xml := XMLTYPE(LV_INPUT);


INSERT INTO TABLE_A (N_SEQ_NO,D_SYSDATE,D_SYSTIMESTAMP, V_VALUE_XML,V_VALUE_STR )
VALUES(1,SYSDATE,SYSTIMESTAMP,LV_XML,SUBSTR(LV_INPUT,1,4000));

SELECT
XMLTYPE.EXTRACT(LV_XML,'/HLACDDRequest/xmlData/xmlDataProcess/Individual/Employer/text()').getstringval() v_name,
XMLTYPE.EXTRACT(LV_XML,'/HLACDDRequest/xmlData/xmlDataProcess/Individual/EmployerAddress/text()').getstringval() V_ADDRESS
INTO LV_NAME,lv_address
FROM DUAL;

UPDATE TABLE_A
SET V_NAME = LV_NAME,
V_ADDRESS = LV_ADDRESS;

END;
/
SELECT * FROM TABLE_A;

Name = ABC&apos;DEF
Address = Xnull
Chris Saxon
November 13, 2025 - 2:45 pm UTC

Use XMLCast ( XMLQuery ( ... ) ) as in my example answer.

Two separate issues; the ampersand is the worse of the two

mathguy, November 13, 2025 - 5:13 pm UTC

Amit - in your original question, you had an issue with the apostrophe. That can be addressed more easily. (As an aside, Oracle escapes apostrophe only when it stores the data, as in your example; if the data is generated on the fly, in a query, then the escaping doesn't happen. Similar to another XML question on AskTom just in the last few days!)

But now you show an example with ampersand. That can't work the way you are trying, and it's not a matter of extracting the data (which should be done with XMLTABLE); rather, your input string is not a valid XML document, so if you apply XMLTYPE to it you'll get an error already. The code you posted in your latest reply errored out for me; are you saying that it ran without error for you, and it only produced the unexpected result? Honestly, I don't understand how that is possible. You have an unescaped ampersand in the text content of a tag. That is invalid XML, and any compliant XML processor should throw an error. (Oracle does!). The ampersand should be represented by &amp; in your input text.

Your question should be, "how do I create a valid XML document to be saved in an XMLTYPE column, so that the data can be extracted from it later." Presumably your user doesn't type an XML document; they type the employer, the address components etc. separately, and you are collecting everything into an XML document, a step you didn't show. That shouldn't be done by simply concatenating hard-coded text with the user's inputs. Instead, proper XML tools for generating XML documents should be used, just the same as the proper XML tools should be used to retrieve the data from the document. Then your user can type ampersand and such, without worrying about XML; and you can generate valid XML documents, from which the data can be extracted later.

You need to take a look at the XML generating tools of Oracle; you may want to start with XMLELEMENT, and expand from there. Specifically regarding your concern about escaping special characters: The XMLELEMENT function has the NOENTITYESCAPING option, which will allow you to create an element like <t>a'bc</t> (instead of <t>a&apos;bc</t>, and it will even allow you to create an element <t>a&bc</t>. But if you do the latter, you will get an invalid XML document, just like the one in your code in the latest post. Oracle allows you to do this, assuming that you are taking responsibility for generating valid XML content; but whoever will try to use such a document later will get errors.

Here is a short illustration of both sides of the process. Note that I "insert" something in table A; in your example, you were "updating", which didn't make a lot of sense to me. (I understand you were using that as an illustration, of course.)

drop table tbl purge;
drop table a purge;

create table tbl (xdoc_id number primary key, xdoc xmltype);
create table a   (employer_name varchar2(40), employer_address varchar2(40));

declare
  e_name varchar2(40) := 'McDonald''s';
  e_addr varchar2(40) := 'Main & Post';
  x_doc  xmltype;
begin
  select xmlelement("Doc", xmlelement("EmployerName", e_name),
                         xmlelement("EmployerAddress", e_addr)
                 )
    into x_doc
    from dual
  ;
  insert into tbl (xdoc_id, xdoc) values (1, x_doc);
  insert into a (employer_name, employer_address)
    select emp_name, emp_addr
    from   tbl,
           xmltable('/Doc' passing xdoc
             columns emp_name varchar2(40) path 'EmployerName',
                     emp_addr varchar2(40) path 'EmployerAddress'
                   )
    where  xdoc_id = 1
  ;
end;
/


Now here's what is in the two tables; notice that the XML document has the entities (escaped characters), but table A shows the values as intended, with the original characters. No manipulation of the characters occurred either on generating the data or on retrieving it; everything is handled by the XML tools on both sides.

set long 200  --  so XDOC is shown in full

select * from tbl;

   XDOC_ID XDOC                                                   
---------- -------------------------------------------------------
         1 <Doc>                                                  
             <EmployerName>McDonald&apos;s</EmployerName>         
             <EmployerAddress>Main &amp; Post</EmployerAddress>   
           </Doc>                                                 



select * from a;

EMPLOYER_NAME                    EMPLOYER_ADDRESS                
-------------------------------- --------------------------------
McDonald's                       Main & Post                     


Chris Saxon
November 14, 2025 - 5:07 pm UTC

Thanks for sharing mathguy