Showing posts with label JSON. Show all posts
Showing posts with label JSON. Show all posts

Saturday, July 25, 2026

Dealing with JSON serialization and how to convert JSON object strings back and forth

A simple reminder about how JSON PL/SQL methods deal with JSON values, it easy to get confused when you mix up JSON objects and their serialized counterparts, especially if some if these parts are coming from JSON SQL functions and you need to combine them with other parts generated by PL/SQL.

The code below should clarify the difference between a "real" JSON value and its textual representation, especially when you are assembling a JSON object with values containing other JSON objects or their serialized representation.

When you PUT a serialized JSON string into a new JSON, the method escapes all the special characters that otherwise would break the syntax (lines 10-16).
In order to reconstruct a valid JSON string, something that you can PARSE as JSON, you need to retrieve the value with GET_STRING or GET_CLOB if it is large (lines 18-20).

If you need to include a serialized JSON object into a new JSON object thus avoiding the automatic escaping, then you need to convert the serialized JSON string into a proper JSON object and then PUT it inside the new object (lines 26-29). 

declare
    c  varchar2(255) := '{"key": 1, "value": "X"}';
    d  varchar2(255);
    j  json_object_t;
    j1 json_object_t;
    j2 json_object_t;
begin
    
    if c is json then
        dbms_output.put_line('c is a string containing a valid JSON');
        j := json_object_t.parse(c);

        j1 := new json_object_t;
        j1.put('document', c);
        dbms_output.put_line('c is now escaped and becomes a string literal value');
        dbms_output.put_line(j1.to_string());
        dbms_output.new_line;
        d := j1.get_string('document');
        dbms_output.put_line('c is converted back into the original JSON object string');
        dbms_output.put_line(d);


        dbms_output.new_line;
        dbms_output.new_line;

        j2 := new json_object_t;
        j2.put('document', j);
        dbms_output.put_line('c is still a json object value, now embedded in a new JSON object');
        dbms_output.put_line(j2.to_string());
    else
        dbms_output.put_line('NOT JSON');
    end if;
end;
/

Watch out for NULL values because GET_STRING and GET_CLOB show two different behaviors in older releases of Oracle 19c.

 

Tuesday, July 22, 2025

stringify() or to_string() ?

While I was experimenting with genAI LLMs applied to generating PL/SQL code, I noticed that Claude-Sonnet created a function containing a stringify() method applied to a JSON_ELEMENT_T variable, a method whose existence I wasn't aware of.
l_string := j_elem.stringify();

This method is documented in the serialization methods paragraph in Oracle23ai JSON Developer's Guide, but it turns out that it works also in Oracle19c (at least on version 19.21.0.0.0) even if the guide doesn't mention it.

As far as I could see it's equivalent to method to_string(), so you can pick whichever you like.

PS:   2025/08/10

Oracle 23ai PL/SQL Packages and Types Reference actually states the following:

"The FUNCTION stringify is synonym of to_String. It has the same functionality."

Monday, July 31, 2023

In a single-byte character set database non-ASCII characters may cause JSON parse to fail

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.
declare
  js json_object_t;
begin
  js := json_object_t.parse('{"name":"$DB_NAME - report GEN - MIELE – Riepilogo domande aiuto"}');
end;
Error report -
ORA-40441: JSON syntax error
ORA-06512: at "SYS.JDOM_T", line 4
ORA-06512: at "SYS.JSON_OBJECT_T", line 86
ORA-06512: at line 4
40441. 00000 -  "JSON syntax error"
*Cause:    The provided JavaScript Object Notation (JSON) data had invalid
           syntax and could not be parsed.
*Action:   Provide JSON data with the correct syntax.

The problem here is the presence of a "en dash" character (ASCII 150) after the word MIELE (it looks like a normal hyphen but it's not).

In a single-character database like this one (WE8MSWIN1252), the presence of non-ASCII characters inside JSON strings causes problems at parsing time.

One solution is to convert the strings containing such characters using the function ASCIISTR, that will replace non-ASCII character with their numeric Unicode counterpart:

report GEN SIAG - MIELE \2013 Riepilogo domande aiuto

However such conversion will increase the length of the string and this might lead to further problems.

Converting the strings using CONVERT(<str>, 'US7ASCII') can also fix the parsing failure and keep the size consistent, but it will replace the non-ASCII character with "?" in most cases. 

In a AL32UTF8 character set database the script above will execute without errors.

Wednesday, May 31, 2023

Handling of non-ASCII characters in JSON objects in a non-AL32UTF8 database

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.
declare
 js   json_object_t;
begin
 js := json_object_t.parse('
{
  "name"       : "whatever",
  "values" : [
              {"name" : "x", "type" : "number", "value" : 2023},
              {"name" : "y", "type" : "string", "value" : "some non-ASCII char àùì"}
             ]
}
');
end;
/

Error report -
ORA-40441: JSON syntax error
ORA-06512: at "SYS.JDOM_T", line 4
ORA-06512: at "SYS.JSON_OBJECT_T", line 86
ORA-06512: at line 4
40441. 00000 -  "JSON syntax error"
*Cause:    The provided JavaScript Object Notation (JSON) data had invalid
           syntax and could not be parsed.
*Action:   Provide JSON data with the correct syntax.

 

Dealing with non-ASCII characters inside JSON objects in a non-AL32UTF8 database requires definitely some extra work, depending also on the final use of the information.

 For instance, if JSON's contents are consumed by a browser, the following escape method works well:

declare
js json_object_t;
begin
js := json_object_t.parse(REGEXP_REPLACE(ASCIISTR('
{
"name" : "whatever",
"values" : [
{"name" : "x", "type" : "number", "value" : 2023},
{"name" : "y", "type" : "string", "value" : "some non-ASCII char àùì"}
]
}
'), '\\([0-9A-F]{4})', '&#x\1;'));
end;
/

Escaping the characters with ASCIISTR only doesn't work because the resulting presence of the "\" characters also breaks the parser as "\" is normally used to escape JSON "reserved" characters, such as \ and ".

If the data need to be converted back from the JSON then the reverse process must be applied:

select
unistr(regexp_replace('some non-ASCII char &#x00E0;&#x00F9;&#x00EC;',
'&#x([0-9A-F]+);',
'\\\1',1,0)) original
from dual;

 

If a lossy character translation is acceptable then CONVERT function could be used.

declare
js json_object_t;
begin
js := json_object_t.parse(CONVERT('
{
"name" : "whatever",
"values" : [
{"name" : "x", "type" : "number", "value" : 2023},
{"name" : "y", "type" : "string", "value" : "some non-ASCII char àùì"}
]
}
','US7ASCII'));
end;
/
-- equivalent to:
declare
 js   json_object_t;
begin
 js := json_object_t.parse('
{
  "name"       : "whatever",
  "values" : [
              {"name" : "x", "type" : "number", "value" : 2023},
              {"name" : "y", "type" : "string", "value" : "some non-ASCII char aui"}
             ]
}
');
end;
/

Please note that in a database with AL32UTF8 character set, the first PL/SQL block would have been processed just fine without the need of escaping anything.

Thursday, June 03, 2021

JSON_QUERY behavior in SQL vs PL/SQL (Oracle 12.2 only?)

Frankly speaking I can't say if this is expected behavior for JSON_QUERY but I find it somewhat inconsistent:

set serveroutput on
begin 
 dbms_output.put_line(json_query('X','$.test'));
end;
/
[] 
select json_query('X', '$.test') v from dual;
====== 
(null)

So, on Oracle 12.2 in PL/SQL the string returned is not null, whilst in SQL is null.

From a test I made later on Oracle 18c the anonymous PL/SQL block above returns null, so I am inclined to consider this a bug of Oracle 12.2.

It's also worth noting that adding NULL ON ERROR clause in Oracle 12.2 doesn't change the result, which is also another possible bug (or a variation of the same problem).

Eventually I chose to use ERROR ON ERROR then catch the exception and return null, because that should avoid the risk of inconsistent results in case the db is upgraded some time in the future.

Friday, December 13, 2019

ORA-40478: output value too large (maximum: )

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

If you are hitting the following error while executing a query containing a JSON_TABLE function like this:

select s.code, 
       s.date_ins, 
       to_char(s.date_ins,'HH24:MI') as t, 
       r.report_name,
       fn_tab2str(cast(multiset(select name || ':' || value 
                                  from json_table(
                                          s.params format json, 
                                          '$[*]' error on error
                                          columns(
                                            name   varchar2(2)  path '$.NAME',
                                            value  varchar2(5)  path '$.VALUE'
                                          )
                                       )
                               ) as ora_mining_varchar2_nt
                      )
                 ) as params
  from batch_statistics s,
       batch_reports r
 where s.code = r.report_code;

ORA-40478: output value too large (maximum: )


it's very likely that you might have specified an insufficient size for a column (in red).
In my case the path $.NAME returns strings of up to 255 bytes, so any value larger than 2 bytes would break the query.


See message translations for ORA-40478 and search additional resources.

Thursday, October 10, 2019

Pretty print JSON strings in Oracle 12.2 the easy way

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

It turns out that in Oracle 12.2 you can easily get pretty printed large JSON strings using a query like this:

select json_query(large_json_value, '$' returning clob pretty) as json_clob
from some_table;

Note however that the syntax returning clob pretty is not mentioned in the documentation, where returning varchar2(n) pretty (with n <= 4000) seems to be the only valid syntax.


I really appreciated the hidden feature because now I can quickly export a bunch of JSON strings  automatically generated with views using nested JSON_ARRAYAGG and JSON_OBJECT SQL functions.


Tuesday, July 30, 2019

JSON, the Euro symbol and a WE8MSWIN1252 character set database

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

Trying to parse a JSON_OBJECT_T string containing the € (euro) character on a WE8MSWIN1252
12.2.0.1.0 database returns the following error:
 
declare
 j json_object_t;
begin
 j := json_object_t.parse('{"currency":"€"}');
end;
/
 
ORA-40441: JSON syntax error
ORA-06512: at "SYS.JDOM_T", line 4
ORA-06512: at "SYS.JSON_OBJECT_T", line 86
ORA-06512: at line 10
40441. 00000 -  "JSON syntax error"
*Cause:    The provided JavaScript Object Notation (JSON) data had invalid
           syntax and could not be parsed.
*Action:   Provide JSON data with the correct syntax.

The only workaround I found consists in converting € into &euro; before parsing and back after retrieving the JSON value.
The same problem occurs with the Yen symbol ¥ and the Sterling Pound symbol £.

I also made a test on an AL32UTF8 database where everything works smoothly without having to handle these symbols in a special way.


The pleasures of working with an 8-bit database.

Wednesday, July 03, 2019

Houston, we've got a problem with JSON PUT method

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

In brief, in Oracle 12.2.0.1 there is some problem with PUT method of JSON_OBJECT_T, for some reason numbers are not recognized as such, but they are stored as scalar.
The workaround is to use JSON_OBJECT_T.PARSE in combination with JSON_OBJECT SQL function for entering numbers (see the script below).
Unfortunately, in turn, JSON_OBJECT_T.PARSE won't recognize dates and timestamps as such, but this behavior is explained in the documentation so it doesn't come as a surprise at least.
See also a previous entry regarding the curious handling of dates and timestamps.


Hopefully Oracle will fix this in a future release.

set SERVEROUTPUT ON
declare
  je     json_element_t;
  jo     json_object_t;
  keys   json_key_list;
begin
  jo := new json_object_t;

  dbms_output.put_line('PUT method doesn''t store "value" as number, but as scalar');
  jo.put('a_string','year');
  jo.put('a_number',2019);   -- this is not stored as number but as scalar, casting as number doesn't fix it.
  jo.put('a_boolean', true);
  jo.put('a_date', sysdate);
  jo.put('a_timestamp', cast(systimestamp as timestamp));  -- curiously you need to cast this value to get it as timestamp

  keys := jo.get_keys;
  for j in 1..keys.count
  loop
  
    je := jo.get(keys(j));
    if je.is_string then
      dbms_output.put_line(keys(j) || ' is string');
    elsif je.is_number then
      dbms_output.put_line(keys(j) || ' is number');
    elsif je.is_date then
      dbms_output.put_line(keys(j) || ' is date');
    elsif je.is_timestamp then
      dbms_output.put_line(keys(j) || ' is timestamp');
    elsif je.is_boolean then
      dbms_output.put_line(keys(j) || ' is boolean');
    elsif je.is_scalar then
      dbms_output.put_line(keys(j) || ' is scalar');
    elsif je.is_object then
      dbms_output.put_line(keys(j) || ' is object');
    elsif je.is_array then
      dbms_output.put_line(keys(j) || ' is array');
    elsif je.is_null then
      dbms_output.put_line(keys(j) || ' is null');
    end if;
    dbms_output.put_line(jo.get(keys(j)).to_string);
  end loop;
  
  dbms_output.put_line('');
  dbms_output.put_line('workaround using PARSE method, correctly stores value as number, it doesn''t recognize a_date as date or a_timestamp as timestamp but as string (this is expected and documented however)');
  jo := json_object_t.parse(json_object('a_string' value 'year', 'a_number' value 2019, 'a_boolean' value false, 'a_date' value sysdate, 'a_timestamp' value systimestamp));

  keys := jo.get_keys;
  for j in 1..keys.count
  loop
  
    je := jo.get(keys(j));
    if je.is_string then
      dbms_output.put_line(keys(j) || ' is string');
    elsif je.is_number then
      dbms_output.put_line(keys(j) || ' is number');
    elsif je.is_date then
      dbms_output.put_line(keys(j) || ' is date');
    elsif je.is_timestamp then
      dbms_output.put_line(keys(j) || ' is timestamp');
    elsif je.is_boolean then
      dbms_output.put_line(keys(j) || ' is boolean');
    elsif je.is_scalar then
      dbms_output.put_line(keys(j) || ' is scalar');
    elsif je.is_object then
      dbms_output.put_line(keys(j) || ' is object');
    elsif je.is_array then
      dbms_output.put_line(keys(j) || ' is array');
    elsif je.is_null then
      dbms_output.put_line(keys(j) || ' is null');
    end if;
    dbms_output.put_line(jo.get(keys(j)).to_string);
  end loop;

end;
/

Thursday, May 16, 2019

JSON get_Date() method always discards time portion

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.


In case you wonder why you don't see the time portion when you call the method get_Date() on a JSON object:

-- tested on Oracle 12.2.0.1.0 
set serveroutput on
declare
  l_job_description   JSON_OBJECT_T;
  l_job_params        JSON_ARRAY_T;
  l_job_arr_elem      JSON_OBJECT_T;
begin
  l_job_description := JSON_OBJECT_T.parse(
'{
  "parameters" : [
                  {"name":"p_date", "type":"date", "value":"2019-12-31T12:01:59"},
                  {"name":"p_dat2", "type":"date", "value":"2019-12-31T12:01:59"}
                 ]
 }');
  l_job_params := l_job_description.get_Array('parameters');
  
  if l_job_params is not null then

    if l_job_params.get_Size > 0 then
    
      for i in 0..l_job_params.get_Size - 1
      loop
  
        l_job_arr_elem := JSON_OBJECT_T(l_job_params.get(i));
        case l_job_arr_elem.get_String('name')
        when 'p_date' then
          dbms_output.put_line(to_char(l_job_arr_elem.get_Date('value'),'YYYY-MM-DD HH24:MI:SS'));
        when 'p_dat2' then
          dbms_output.put_line(to_char(l_job_arr_elem.get_Timestamp('value'),'YYYY-MM-DD HH24:MI:SS'));
        end case;
  
      end loop;
    
    end if;

  end if;

end;
/
 
2019-12-31 00:00:00
2019-12-31 12:01:59


Method get_Timestamp() will come to the rescue.

Tuesday, November 10, 2015

Error: parsererror - SyntaxError: JSON.parse: unexpected non-whitespace character after JSON data at line 2 column 1 of the JSON data

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

Error: parsererror - SyntaxError: JSON.parse: 
unexpected non-whitespace character after JSON data at line 2 column 1 of the JSON data

You may get this self-explanatory error at run-time if you specified a non-existing page item in the list of items to be returned after invoking a PL/SQL procedure from within a dynamic action in Oracle Application Express.




This may easily happen if you mistyped the page item name for instance, but in my case I had completely forgotten to create the item P4_ID.
Luckily I had an epiphany before going totally nuts.

yes you can!

Two great ways to help us out with a minimal effort. Click on the Google Plus +1 button above or...
We appreciate your support!

latest articles