Need help with your JSON?
Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool
PL/SQL JSON Functions in Oracle Databases
If you are searching for PL/SQL JSON functions in Oracle, the first thing to know is that Oracle has two JSON toolsets. SQL/JSON functions such as JSON_OBJECT, JSON_VALUE, JSON_TABLE, and JSON_TRANSFORM run inside SQL statements. PL/SQL object types such as JSON_OBJECT_T and JSON_ARRAY_T let you parse, inspect, and mutate JSON in PL/SQL code.
The version boundary matters. In Oracle 19c and other pre-21c deployments, JSON is typically stored in VARCHAR2, CLOB, or BLOB columns validated with IS JSON. In Oracle 21c and later, Oracle adds a native JSON data type backed by binary JSON, which is the better default for new designs when your environment supports it.
Quick Answer: Which Oracle JSON Feature Should You Use?
- Use
JSON_OBJECT,JSON_ARRAY,JSON_OBJECTAGG, andJSON_ARRAYAGGto generate JSON from relational data. - Use
JSON_VALUEfor a scalar,JSON_QUERYfor an object or array,JSON_EXISTSfor predicates, andJSON_TABLEwhen you need rows and columns. - Use
JSON_TRANSFORMwhen your database version supports it and you want a declarative, ordered, atomic update to a JSON document. - Use
JSON_OBJECT_TandJSON_ARRAY_Tin PL/SQL when the logic is procedural, conditional, or easier to express in code than in one SQL statement. - Use a
RETURNINGclause for large results. Without it, many SQL/JSON functions on textual JSON default toVARCHAR2(4000), which is often too small for real payloads.
Version-Aware Compatibility
A lot of confusion around Oracle JSON comes from mixing guidance for 19c and 21c+. The table below is the short version that matters most in practice.
| Oracle Version | What You Can Rely On | Practical Default |
|---|---|---|
| 19c and older long-lived deployments | JSON in VARCHAR2, CLOB, or BLOB with IS JSON; SQL/JSON query and generation functions; PL/SQL JSON object types | Store larger documents in CLOB or BLOB, validate with IS JSON, and use SQL/JSON for querying |
| 21c and later | Native JSON data type, binary JSON storage, JSON_TRANSFORM, and bridge methods between PL/SQL object types and SQL JSON values | Prefer the native JSON type for new tables if your database compatibility settings allow it |
The native JSON type is not a 19c feature. If you are maintaining a 19c system, plan around text or binary large object storage plus IS JSON validation.
Storing JSON Data Correctly
Storage choice is the first design decision. If you are on 21c+, the native JSON type is usually the right answer. If you are on 19c, use a textual or binary column and enforce validity with IS JSON.
Example: 19c Storage vs 21c+ Storage
-- Oracle 19c-style table design
CREATE TABLE orders_text (
order_id NUMBER PRIMARY KEY,
order_doc CLOB CHECK (order_doc IS JSON)
);
-- Oracle 21c+ table design
CREATE TABLE orders_native (
order_id NUMBER PRIMARY KEY,
order_doc JSON
);
-- If you only want JSON objects/arrays in a text column, tighten validation
-- with DISALLOW SCALARS instead of accepting any RFC 8259 scalar document.
-- Example:
-- CHECK (order_doc IS JSON DISALLOW SCALARS)
On 21c+, top-level JSON scalars are supported when the database compatible setting is high enough. If your application expects only objects or arrays, enforce that rule explicitly instead of assuming the default shape.
Creating JSON with SQL/JSON Functions
For most reporting and API-style queries, generate JSON in SQL instead of manually concatenating strings. Oracle's SQL/JSON generation functions are safer, easier to maintain, and version-stable.
- JSON_OBJECT: Build an object from name/value pairs.
- JSON_ARRAY: Build an array from literal or expression values.
- JSON_ARRAYAGG: Aggregate rows into a JSON array.
- JSON_OBJECTAGG: Aggregate key/value pairs into a JSON object.
Example: Generate an Order Document
SELECT JSON_OBJECT(
'orderId' VALUE o.order_id,
'customerId' VALUE o.customer_id,
'items' VALUE JSON_ARRAYAGG(
JSON_OBJECT(
'sku' VALUE i.sku,
'qty' VALUE i.qty
)
ORDER BY i.line_no
)
ABSENT ON NULL
RETURNING CLOB
) AS order_json
FROM orders o
JOIN order_items i
ON i.order_id = o.order_id
WHERE o.order_id = 1001
GROUP BY o.order_id, o.customer_id;
ABSENT ON NULL is often the better default for API payloads because it removes missing keys instead of emitting JSON null. If you need explicit nulls, use NULL ON NULL.
Querying JSON Data
Query functions are where most Oracle JSON work happens. The main rule is simple: use the function that matches the shape you expect back.
- JSON_VALUE: Return a scalar such as a string or number.
- JSON_QUERY: Return a JSON object or array.
- JSON_EXISTS: Test whether a path exists or a predicate matches.
- JSON_TABLE: Project JSON into rows and columns and then query it relationally.
- JSON_SERIALIZE: Convert JSON to display text, including pretty printed output.
Example: Extract Scalars, Arrays, and Rows
-- Scalar extraction
SELECT order_id,
JSON_VALUE(order_doc, '$.customerId' RETURNING NUMBER) AS customer_id
FROM orders_text;
-- Return a nested array as JSON text
SELECT order_id,
JSON_QUERY(order_doc, '$.items' RETURNING CLOB PRETTY) AS items_json
FROM orders_text;
-- Relational projection of an array
SELECT o.order_id, jt.sku, jt.qty
FROM orders_text o
CROSS APPLY JSON_TABLE(
o.order_doc,
'$.items[*]'
COLUMNS (
sku VARCHAR2(30) PATH '$.sku',
qty NUMBER PATH '$.qty'
)
) jt;
-- Pretty-print a full JSON document for inspection
SELECT JSON_SERIALIZE(order_doc PRETTY RETURNING CLOB)
FROM orders_native
WHERE order_id = 1001;
JSON_VALUE is only for scalars. If the path points to an object or array, switch to JSON_QUERY or JSON_TABLE. This is one of the most common Oracle JSON mistakes.
Updating Documents with JSON_TRANSFORM
When your database version includes JSON_TRANSFORM, use it for document updates instead of rebuilding the whole payload manually. Oracle documents the operations as ordered and atomic, which makes it a safer fit for multi-step edits.
Example: Change Status and Remove a Temporary Field
UPDATE orders_native
SET order_doc = JSON_TRANSFORM(
order_doc,
SET '$.status' = 'shipped',
REMOVE '$.draftNote'
)
WHERE order_id = 1001;
-- Preview the result without writing it back
SELECT JSON_TRANSFORM(
order_doc,
SET '$.status' = 'shipped',
REMOVE '$.draftNote'
) AS updated_doc
FROM orders_native
WHERE order_id = 1001;
If you are on a 19c estate and do not have JSON_TRANSFORM available in your deployed environment, fall back to SQL generation functions or PL/SQL object types for the rewrite.
Working with JSON in PL/SQL
This is the part many articles skip. When the requirement is truly PL/SQL, Oracle's JSON object types are often more ergonomic than packing everything into one SQL statement.
JSON_ELEMENT_Tis the supertype for parsed JSON values.JSON_OBJECT_Trepresents a JSON object.JSON_ARRAY_Trepresents a JSON array.- Getter methods such as
get_stringandget_numberreturn typed values, whileget()returns a JSON element you can inspect or cast.
Example: Parse, Mutate, and Serialize in PL/SQL
DECLARE
l_doc JSON_OBJECT_T;
l_items JSON_ARRAY_T;
BEGIN
l_doc := JSON_OBJECT_T.parse(
'{"orderId":1001,"status":"new","items":[{"sku":"A-1","qty":2}]}'
);
l_doc.put('status', 'validated');
l_items := TREAT(l_doc.get('items') AS JSON_ARRAY_T);
l_items.append(JSON_OBJECT_T.parse('{"sku":"B-9","qty":1}'));
DBMS_OUTPUT.put_line(l_doc.to_string);
END;
/
Use to_clob() instead of to_string() for larger payloads. On 21c+, Oracle also documents bridge methods such as load() and to_json for moving between PL/SQL object types and the native SQL JSON data type.
Indexing and Performance Tips
Oracle JSON performance usually comes down to two things: not reparsing more than necessary and indexing the paths you actually filter on.
Example: Index a Frequently Filtered Scalar Path
CREATE INDEX orders_customer_id_idx
ON orders_text (
JSON_VALUE(order_doc, '$.customerId' RETURNING NUMBER)
);
-- Write the query with the same path and return type so Oracle can use the index.
SELECT order_id
FROM orders_text
WHERE JSON_VALUE(order_doc, '$.customerId' RETURNING NUMBER) = 42;
- If you filter on one or two stable scalar paths, a function-based index or virtual column is often the cleanest choice.
- If you need broader search across many paths, evaluate Oracle's JSON search index instead of stacking many narrow indexes.
- If you extract multiple fields from the same document in one query,
JSON_TABLEis usually a better design than repeating several unrelatedJSON_VALUEcalls. - On newer versions, native
JSONstorage reduces parsing overhead compared with repeatedly reading large textual JSON documents.
Common Pitfalls and Debugging Notes
- Silent nulls are dangerous. During development, prefer
ERROR ON ERRORor explicit defaults instead of relying on forgiving behavior. - Large JSON generation queries should usually specify
RETURNING CLOBor, on native JSON systems,RETURNING JSON. - If a query plan ignores your JSON index, check that the indexed expression and the predicate use the same path and compatible return type.
- If you are debugging payload shape, use
JSON_SERIALIZE(... PRETTY)in SQL or your formatter tool outside the database to inspect the exact output. - If the application only stores objects and arrays, do not leave that rule implicit. Enforce it with validation instead of letting scalar documents slip in by accident.
Conclusion
The best answer to "PL/SQL JSON functions in Oracle" is not one function name. It is a working split: generate and query JSON with SQL/JSON functions, use PL/SQL object types for procedural manipulation, and choose storage based on whether you are on 19c-style text JSON or 21c+ native JSON. Once you anchor the design to the right Oracle version, the rest of the function choices become much clearer.
Need help with your JSON?
Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool