Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

PL/SQL & T-SQL (Oracle and SQL Server)  Module 15 – Production PL/SQL: staging, concurrency, JSON and tests

JSON in PL/SQL: object types and 26ai features

Parse, change and serialise JSON with PL/SQL's JSON object types, turn records into JSON in 26ai, and keep a fallback for code that must run on 19c.

  • Intermediate
  • 20 minutes
  • Examples run with oracle-free 23.26.3.0.0, mssql 17.0.5005.3, Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free core, Pro depth: 8 of 20 minutes free
Free: JSON object types turn text into a tree you can change · What 26ai adds that PL/SQL code meets first. With a pass: 2 in-depth parts and 3 interview answers, about 12 minutes.

What you will learn

  • Parse, change and serialise JSON with JSON_OBJECT_T and JSON_ARRAY_T
  • Map records and collections to JSON with the 26ai JSON constructor and JSON_VALUE
  • Use 26ai SQL features from PL/SQL: BOOLEAN, IF [NOT] EXISTS and SELECT without FROM
  • Write code that still runs on 19c where it must

Before you start

What you’ll learn · Before you start

What you will learn

  • Parse, change and serialise JSON with JSON_OBJECT_T and JSON_ARRAY_T
  • Map records and collections to JSON with the 26ai JSON constructor and JSON_VALUE
  • Use 26ai SQL features from PL/SQL: BOOLEAN, IF [NOT] EXISTS and SELECT without FROM
  • Write code that still runs on 19c where it must

Before you start

On this page

Order A-17 reaches a package as JSON text with two items of quantity 1 and 2, and the package must add "total_qty": 3 before it passes the order on. JSON in PL/SQL does that with the JSON object types, which parse the text into an object the code can read and change, then turn it back into text.

You will total the order with them step by step, then meet the 26ai features that PL/SQL code uses first: SQL BOOLEAN, IF [NOT] EXISTS, SELECT without FROM, VALUES rows and records that become JSON, each with what a 19c site uses instead.

JSON object types turn text into a tree you can change

JSON object types: The PL/SQL object types for JSON: JSON_ELEMENT_T and its subtypes JSON_OBJECT_T, JSON_ARRAY_T and JSON_SCALAR_T, which hold a parsed document in memory so that code can read, change and serialise it. hold a JSON document in memory. JSON_OBJECT_T.parse reads the order’s text and returns an object; get functions read its fields, put changes them and to_String turns the whole object back into text. Oracle’s JSON Data Structures page lists five types:

Type What it holds
JSON_ELEMENT_T any parsed value; the supertype of the next three
JSON_OBJECT_T an object: get_Number, get_Array, has, put
JSON_ARRAY_T an array: get(i), get_size, append
JSON_SCALAR_T one string, number, true, false or null
JSON_KEY_LIST the key names that get_Keys returns

Totalling the order takes six calls, two of them inside a loop. Step through them:

Interactive · stage by stage Totalling order A-17 with JSON object types Step through the six calls. The loop arrow runs once for each position of the items array, from 0.
Totalling order A-17 with JSON object types, step 8 of 8. Showing parse the order text, get_Array('items'), get(i), TREAT (position 1, TEA-250), get_Number('qty') (running total 3), put('total_qty', 3), to_String. In focus: to_String.next positionparse the order texta JSON_OBJECT_Tget_Array('items')a JSON_ARRAY_T of 2get(i), TREATposition 1, TEA-250get_Number('qty')running total 3put('total_qty', 3)added as the last keyto_Stringthe order as text again

The drawing shows the whole scene; its 8 steps are listed below.

Every step as text
  1. Parse the text. JSON_OBJECT_T.parse reads the order's text and builds an object in memory. Text that is not a valid JSON object makes parse raise an error.
  2. Get the items array. get_Array('items') returns the array under the key items, a JSON_ARRAY_T whose get_size is 2.
  3. Position 0 first. get(0) returns the first item as a JSON_ELEMENT_T, and TREAT turns it into a JSON_OBJECT_T so that its fields can be read.
  4. Add its quantity. get_Number('qty') reads 1, and the running total becomes 1.
  5. Then position 1. The loop moves to position 1, the last one, because the loop runs from 0 to get_size - 1.
  6. Add the second quantity. get_Number('qty') reads 2, and the running total becomes 3. The loop has read every position.
  7. Put the total back. put adds the key total_qty with the value 3. A new key goes at the end of the object; an existing one would be overwritten.
  8. Serialise it again. to_String turns the object back into text, with total_qty after items, ready to pass on.

Positions in a JSON_ARRAY_T start at 0: Oracle’s own example walks an array with FOR i IN 0 .. get_size - 1. get(i) returns each item as a JSON_ELEMENT_T, so TREAT makes it a JSON_OBJECT_T before get_Number('qty') reads it, and put adds total_qty as the last key. Here is the block, then a Python model of the same steps that prints each position it reads. Press Edit, set FIRST_POSITION = 1 and run it again:

order_json.sql: parse order A-17, total its quantities, serialise it again SQL · json/order_json.sql
-- Engine: Oracle AI Database 26ai Free, Release 23.26.3.0.0; user LAB in FREEPDB1. Client: SQL*Plus or SQLcl.
-- Order A-17 arrives as JSON text. The block parses it into JSON object types, reads every item of the "items"
-- array, adds the quantities and puts the total back into the document as "total_qty", then serialises it again.
-- It changes no table, so it can run at any time.
SET SERVEROUTPUT ON
SET LINESIZE 120

DECLARE
  v_order JSON_OBJECT_T;
  v_items JSON_ARRAY_T;
  v_item  JSON_OBJECT_T;
  v_total NUMBER := 0;
BEGIN
  v_order := JSON_OBJECT_T.parse(
    '{"order":"A-17","items":[{"sku":"RICE-5K","qty":1},{"sku":"TEA-250","qty":2}]}');
  v_items := v_order.get_Array('items');

  -- Array positions start at 0, as Oracle's own examples loop: 0 .. get_size - 1.
  FOR i IN 0 .. v_items.get_size - 1 LOOP
    v_item  := TREAT(v_items.get(i) AS JSON_OBJECT_T);
    v_total := v_total + v_item.get_Number('qty');
  END LOOP;

  v_order.put('total_qty', v_total);
  DBMS_OUTPUT.PUT_LINE('items: ' || v_items.get_size);
  DBMS_OUTPUT.PUT_LINE(v_order.to_String);
END;
/
-- Expected:
-- items: 2
-- {"order":"A-17","items":[{"sku":"RICE-5K","qty":1},{"sku":"TEA-250","qty":2}],"total_qty":3}
order_json.sql, run in the lab Python · order_json_session.py
"""order_json.sql in the lab: SQL*Plus runs it as LAB on a freshly loaded practice schema."""

from oracle_session import Lab

with Lab() as lab:
    lab.sqlplus("json/order_json.sql")

Output

$ sqlplus -s lab@//localhost/FREEPDB1 @json/order_json.sql
items: 2
{"order":"A-17","items":[{"sku":"RICE-5K","qty":1},{"sku":"TEA-250","qty":2}],"total_qty":3}

PL/SQL procedure successfully completed.

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 order_json_session.py

Order A-17 through the same steps, modelled in Python Python · model/order_model.py
"""Order A-17 through the steps of order_json.sql, modelled in Python.

A model, not PL/SQL: Python's json module stands in for JSON_OBJECT_T and JSON_ARRAY_T. It parses the text, reads the
"items" array from position FIRST_POSITION to the last one (positions start at 0, as in Oracle's own examples), adds
the quantities, puts "total_qty" at the end of the object, as JSON_OBJECT_T.put does for a new key, and serialises the
document without spaces, as to_String does.

Press Edit, change a quantity in ORDER, or set FIRST_POSITION = 1 (the slip of counting from 1), and run it again.
"""

import json

ORDER = '{"order":"A-17","items":[{"sku":"RICE-5K","qty":1},{"sku":"TEA-250","qty":2}]}'
FIRST_POSITION = 0

order = json.loads(ORDER)
items = order["items"]
total = 0
for position in range(FIRST_POSITION, len(items)):
    item = items[position]
    total += item["qty"]
    print(f"position {position}: {item['sku']} qty {item['qty']}, running total {total}")

order["total_qty"] = total
print(f"items: {len(items)}")
print(json.dumps(order, separators=(",", ":")))
done = True

Output

position 0: RICE-5K qty 1, running total 1
position 1: TEA-250 qty 2, running total 3
items: 2
{"order":"A-17","items":[{"sku":"RICE-5K","qty":1},{"sku":"TEA-250","qty":2}],"total_qty":3}

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 order_model.py

Counting array positions from 1

Wrong: FOR i IN 1 .. v_items.get_size - 1, counting from 1 as PL/SQL collections do. Position 0, RICE-5K, is never read, and the model prints "total_qty":2. Right: FOR i IN 0 .. v_items.get_size - 1.

The 19c reference documents the same five types, so JSON in PL/SQL written with them runs on both releases. To look at a document before you write code for it, format it in the JSON formatter.

Checkpoint

Check: Parsing, reading and changing JSON with object types

  1. Question 1 of 2 An order's items array holds 4 items. A loop reads FOR i IN 1 .. v_items.get_size - 1. How many items does it read?

    Type a number.

    A hint

    Positions run from 0 to get_size - 1.

    Show the answer: Question 1 of 2

    Answer: 3

    The positions are 0, 1, 2 and 3. Starting at 1 reads positions 1, 2 and 3, so 3 items, and the first one is never added.

  2. Question 2 of 2 Why does the block apply TREAT to the result of v_items.get(i)?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: get returns a JSON_ELEMENT_T, and TREAT makes it a JSON_OBJECT_T whose fields get_Number can read

    Oracle's page says that casting a JSON_ELEMENT_T to a subtype needs an explicit TREAT ... AS; after it, the object's get functions are available.

What 26ai adds that PL/SQL code meets first

Oracle’s 26ai New Features guide and the 26ai PL/SQL changes list five features that everyday PL/SQL code meets. Each does one plain thing, and the last column gives what a 19c database offers instead:

Feature What it does On 19c
SQL BOOLEAN a column type for TRUE, FALSE or NULL no BOOLEAN in SQL
IF [NOT] EXISTS DDL that skips an object already there, or already gone from Release Update 19.28
SELECT without FROM SELECT 3 * 7 runs on its own SELECT 3 * 7 FROM dual
VALUES rows VALUES (1), (2), (4) builds rows without a table one-row queries and UNION ALL
JSON for PL/SQL types a record or collection becomes JSON, and JSON comes back as one a put or get call per field

A VALUES clause like that is a table value constructor: A VALUES clause that builds rows on the fly without a table, such as VALUES (1), (2), (4). Oracle accepts it in SELECT, INSERT and MERGE statements from 26ai., which Oracle accepts in SELECT, INSERT and MERGE. The releases, in the order they brought these features:

Timeline · 6 events in order Releases this lesson's code meets Walk through the releases in order. Each event names what that release added for code like the lesson's.
  1. 19c

    The long-term release with JSON object types baseline

    The 19c reference already documents the JSON object types, JSON_OBJECT_T and JSON_ARRAY_T among them, so code that parses, reads and changes JSON with them runs on 19c and on 26ai alike.

  2. 19.7

    SQL table macros release update

    The 19c PL/SQL changes page says SQL table macros are supported from version 19.7, a release update of 19c rather than a new release.

  3. 19.28

    IF [NOT] EXISTS on DDL release update

    From Release Update 19.28, CREATE, ALTER and DROP accept IF NOT EXISTS and IF EXISTS, so a script can run twice without failing on an object that is already there, or already gone.

  4. 21c

    Iteration controls new syntax

    The 21c PL/SQL changes add new iteration controls, such as stepped ranges and chained iterators, in loops and in qualified expressions.

  5. 26ai

    SQL BOOLEAN and JSON for PL/SQL types new syntax

    The 26ai changes add the BOOLEAN type to SQL, SELECT without a FROM clause, VALUES constructors, extended CASE controls, and a JSON constructor and JSON_VALUE that work with PL/SQL records and collections.

  6. 26ai RU 23.26.0

    The RESETTABLE clause release update

    Release Update 23.26.0 of 26ai adds RESETTABLE, which marks a package whose state may be discarded during a patch without raising ORA-04068.

The timeline has 6 events, from 19c to 26ai RU 23.26.0.

Three larger 26ai features belong to other tracks: SQL domains declare what a column holds, such as an email address; JSON-relational duality views show the same data as documents or as rows; AI Vector Search queries data by meaning. Three topics of this lesson go deeper. A record or collection becomes JSON in a shape that depends on its kind. A get call has a fixed behaviour when a key is missing or a value has the wrong type. And conditional compilation lets one source compile on both releases.

Checkpoint

Check: The 26ai features PL/SQL code meets, and 19c

  1. Question 1 of 2 A table needs a yes-or-no column on a 19c database. What do you declare?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: NUMBER(1) with a check that allows only 0 and 1

    Oracle's 26ai changes page says the BOOLEAN data type is now supported by SQL; on 19c, a NUMBER(1) flag with a check constraint does the same work.

  2. Question 2 of 2 A deployment script with CREATE TABLE IF NOT EXISTS must run on a 19c database at Release Update 19.30. Does it work there?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Yes, because IF [NOT] EXISTS reached 19c in Release Update 19.28

    Name the release and, on 19c, the release update: from 19.28 a 19c database accepts IF NOT EXISTS too.

Records and collections as JSON, kind by kind, and the checks on the way back Pro: opens with a Pro, Premium or Ultimate pass

In 26ai the JSON constructor accepts a PL/SQL record or collection, and JSON_VALUE can return one, which replaces a put or a get call for every field. Each kind of value has its own rule for the JSON it becomes, and the way back checks every field.

In this part:

  • The JSON a record becomes, field by field
  • The JSON shapes of varrays and nested tables
  • Reading JSON back into a record, field by field

3 runnable examples (Python, SQL) · about 5 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Silent NULLs, shared references, and one source for 19c and 26ai Pro: opens with a Pro, Premium or Ultimate pass

Two things break JSON code that works on the test order: documents whose keys or values differ from the ones the code expects, and a database that still runs 19c. Both are reproduced below with their fixes, together with the rule for what a get call shares with the document it reads.

In this part:

  • What a get call returns when the key is missing
  • Which calls share the document and which copy it
  • One source for 19c and 26ai with conditional compilation

5 runnable examples (Python, SQL) · about 7 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Practise: read the tree and pick the release

Each question gives a call, a loop or a statement. Say what it returns, or which release runs it:

Practice that adapts to you

Practise this lesson

6 questions about 7 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 6 What does JSON_OBJECT_T.parse return for the text of an order?

    Choose one answer.

    Show the answer: Question 1 of 6

    Answer: A JSON_OBJECT_T held in memory, whose fields the code can read and change

    parse turns text into an object tree; get functions, put and to_String do the rest.

  2. Question 2 of 6 An order's items carry qty 2, 5 and 1. What total_qty does the lesson's block put into the document?

    Type a number.

    Show the answer: Question 2 of 6

    Answer: 8

    The loop reads positions 0, 1 and 2 and adds 2 + 5 + 1 = 8, then put adds total_qty as the last key.

  3. Question 3 of 6 An order arrives with an empty items array. What does the lesson's block do?

    Choose one answer.

    Show the answer: Question 3 of 6

    Answer: The loop runs no pass, and total_qty is 0

    A FOR loop whose lower bound is above its upper bound runs no pass, so an empty array gives a total of 0.

  4. Question 4 of 6 How do you write SELECT 3 * 7 so that it also runs on 19c?

    Choose one answer.

    Show the answer: Question 4 of 6

    Answer: SELECT 3 * 7 FROM dual

    SELECT without FROM is new in 26ai; FROM dual works on both releases.

  5. Question 5 of 6 How many rows does SELECT n FROM (VALUES (1), (2), (4)) t (n) return?

    Type a number.

    Show the answer: Question 5 of 6

    Answer: 3

    Each bracketed row value is one row, so the table value constructor builds three rows: 1, 2 and 4.

  6. Question 6 of 6 A package must run on a 19c database at Release Update 19.28. Which of these can it use there?

    Choose every answer that is right.

    Show the answer: Question 6 of 6

    Answer:

    • CREATE TABLE IF NOT EXISTS
    • JSON_OBJECT_T and JSON_ARRAY_T

    On 19c at 19.28, the object types and IF [NOT] EXISTS work; SQL BOOLEAN and SELECT without FROM need 26ai.

Interview questions

Warm-up (fresher to 2–5 years): when do you reach for JSON_OBJECT_T.parse, and when for JSON_VALUE?

Parse when PL/SQL must read or change several parts of one document: JSON_OBJECT_T.parse builds the whole tree, and the code walks it, tests keys with has, changes it with put and serialises it with to_String. Use JSON_VALUE when one scalar is enough: it is a SQL/JSON function that takes a path such as '$.order' and returns that single value, in a query or in PL/SQL. Building a document piece by piece needs the object types, because JSON_VALUE only reads.

Three longer questions follow: a renamed field that valued orders at zero, one package for 19c and 26ai sites, and a batch that parses the same document again for every field:

  1. Mid to senior

    An order package values each line as NVL(item.get_Number('unit_price'), 0) * qty. After a partner renamed the key to price, 340 orders were valued at 0.00 and skipped the payment step, and nothing was logged. Explain what happened, and how you would make the next such change fail at once.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Senior to staff

    Your package runs on three 26ai databases and two 19c databases at Release Update 19.24. The team wants SQL BOOLEAN columns, CREATE TABLE IF NOT EXISTS in its scripts and JSON(v_line) to turn records into JSON. What do you write today, and what do you test on each release?

    Model answer, Pro. First step and follow-ups

  3. Mid to senior

    A function reads 12 fields from a 200 KB order document, and for each field it calls JSON_OBJECT_T.parse on the document's text again. A nightly batch of 50,000 orders now runs for hours. What is wrong, and how do you fix it?

    Model answer, Pro. First step and follow-ups

Flashcards · 8 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. What are the three steps of changing a JSON document in PL/SQL?

    JSON_OBJECT_T.parse reads the text into an object held in memory, get functions read its fields and put changes them, and to_String turns the whole object back into text.

  2. Which five JSON object types does Oracle document?

    JSON_ELEMENT_T, any parsed value and the supertype of the next three; JSON_OBJECT_T with get_Number, get_Array, has and put; JSON_ARRAY_T with get(i), get_size and append; JSON_SCALAR_T for one string, number, true, false or null; and JSON_KEY_LIST, the key names get_Keys returns.

  3. Where do positions in a JSON_ARRAY_T start, and what does get(i) return?

    At 0, so Oracle's own example walks an array with FOR i IN 0 .. get_size - 1; counting from 1 as PL/SQL collections do silently skips the first item. get(i) returns a JSON_ELEMENT_T, so TREAT makes it the subtype you need before reading a field.

  4. Do the JSON object types work on 19c?

    Yes: the 19c reference documents the same five types, so JSON code written with them runs on both releases.

  5. Which five 26ai features does everyday PL/SQL code meet?

    A SQL BOOLEAN column type; IF [NOT] EXISTS on DDL; SELECT without FROM; VALUES rows that build rows without a table; and JSON for PL/SQL types, so a record or collection becomes JSON and comes back as one.

  6. What does a 19c database offer in place of those 26ai features?

    No BOOLEAN in SQL; IF [NOT] EXISTS from Release Update 19.28; SELECT 3 * 7 FROM dual instead of a bare SELECT; one-row queries with UNION ALL instead of VALUES rows; and a put or get call per field instead of mapping a whole record.

  7. What is a VALUES clause used as a source of rows called, and where is it accepted?

    A table value constructor, which Oracle accepts in SELECT, INSERT and MERGE.

  8. Which three larger 26ai features belong to other tracks?

    SQL domains, which declare what a column holds such as an email address; JSON-relational duality views, which show the same data as documents or as rows; and AI Vector Search, which queries data by meaning.

Key takeaways

  • JSON in PL/SQL starts with JSON_OBJECT_T.parse: get functions read the tree, put changes it and to_String turns it back into text.
  • Array positions start at 0, and get(i) returns a JSON_ELEMENT_T that TREAT turns into the subtype you need.
  • 26ai adds SQL BOOLEAN, SELECT without FROM, VALUES rows and JSON for PL/SQL records and collections, while IF [NOT] EXISTS reaches 19c from Release Update 19.28.
  • The JSON object types are documented on 19c too, so code written with them runs on both releases.

Terms in this lesson

JSON object types
The PL/SQL object types for JSON: JSON_ELEMENT_T and its subtypes JSON_OBJECT_T, JSON_ARRAY_T and JSON_SCALAR_T, which hold a parsed document in memory so that code can read, change and serialise it.
table value constructor
A VALUES clause that builds rows on the fly without a table, such as VALUES (1), (2), (4). Oracle accepts it in SELECT, INSERT and MERGE statements from 26ai.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.