Took a bit of figuring out (more used to PostgreSQL where things are much easier!), but if you consult the fine manual here, under 12.16.2 Functions That Create JSON Values, there's the JSON_ARRAY function, but it's not much use really - at least in this case!

To answer the question "select and it return JSON", there are two ways of doing this, both rather painful!

You can either

  • use a "hack" - see the db-fiddle here,

  • or use one of the new MySQL supplied JSON functions here - which, ironically, appears to be even more of a hack than the hack itself! Only with MySQL! :-) (fiddle here).

Both answers use the MySQL GROUP_CONCAT function - this post helped. You might want to set the group_concat_max_len system variable to a bit more than its default (a paltry 1024)!

The first query is, as you can imagine, messy (DDL and DML at the bottom of this answer):

SELECT CONCAT('[', better_result, ']') AS best_result FROM
(
SELECT GROUP_CONCAT('{', my_json, '}' SEPARATOR ',') AS better_result FROM
(
  SELECT 
    CONCAT
    (
      '"name_field":'   , '"', name_field   , '"', ',' 
      '"address_field":', '"', address_field, '"', ','
      '"contact_age":'  , contact_age
    ) AS my_json
  FROM contact
) AS more_json
) AS yet_more_json;

Result:

[{"name_field":"Mary","address_field":"address one","contact_age":25},{"name_field":"Fred","address_field":"address two","contact_age":35},{"name_field":"Bill","address_field":"address three","contact_age":47}]

which is correct, but, let's face it, a bit of a nightmare!

Then there's the MySQL JSON_ARRAY() approach (which is even messier - thanks MySQL for your (non-existent) implementation of the TRANSLATE() function!).

SELECT
CONCAT
('[', REPLACE
  (
    REPLACE
    (
      GROUP_CONCAT
      (
        JSON_ARRAY
        (
          'name_field:', name_field, 
          'address_field:', address_field, 
          'age_field:', contact_age
        ) SEPARATOR ','
      ), '[', '{'
    ), ']', '}'
  ), ']'
) 
AS best_result2 
FROM contact

Same result!

====  TABLE CREATION and INSERT DDL and DML ============

    CREATE TABLE contact
    (
         name_field VARCHAR  (5) NOT NULL,
      address_field VARCHAR (20) NOT NULL,
      contact_age   INTEGER      NOT NULL
    );

    INSERT INTO contact
    VALUES
    ('Mary', 'address one',   25),
    ('Fred', 'address two',   35),
    ('Bill', 'address three', 47);
Answer from Vérace on Stack Exchange
🌐
MySQL
dev.mysql.com › doc › refman › 8.4 › en › json-search-functions.html
MySQL :: MySQL 8.4 Reference Manual :: 14.17.3 Functions That Search JSON Values
This functionality is not limited to SELECT, as shown here: mysql> ALTER TABLE jemp ADD COLUMN n INT; Query OK, 0 rows affected (0.68 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> UPDATE jemp SET n=1 WHERE c->"$.id" = "4"; Query OK, 1 row affected (0.04 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> SELECT c, c->"$.id", g, n > FROM jemp > WHERE JSON_EXTRACT(c, "$.id") > 1 > ORDER BY c->"$.name"; +-------------------------------+-----------+------+------+ | c | c->"$.id" | g | n | +-------------------------------+-----------+------+------+ | {"id": "3", "name": "Barney"} | "3" | 3 | NULL
🌐
MySQL Tutorial
mysqltutorial.org › home › mysql json
MySQL JSON
November 11, 2023 - JSON Data Type – Introduction to JSON data type in MySQL. JSON_ARRAY() – Create JSON arrays from a list of values.
🌐
MySQL
dev.mysql.com › doc › refman › 9.6 › en › json-utility-functions.html
MySQL :: MySQL 9.6 Reference Manual :: 14.17.8 JSON Utility Functions
This means that this function always shows the space currently used to store a JSON document in a user variable: mysql> SET @j = '[100, "sakila", [1, 3, 5], 425.05]'; Query OK, 0 rows affected (0.00 sec) mysql> SELECT @j, JSON_STORAGE_SIZE(@j) AS Size; +------------------------------------+------+ | @j | Size | +------------------------------------+------+ | [100, "sakila", [1, 3, 5], 425.05] | 45 | +------------------------------------+------+ 1 row in set (0.00 sec) mysql> SET @j = JSON_SET(@j, '$[1]', "json"); Query OK, 0 rows affected (0.00 sec) mysql> SELECT @j, JSON_STORAGE_SIZE(@j) AS S
🌐
DB Vis
dbvis.com › thetable › json_extract-mysql-function-complete-guide
JSON_EXTRACT MySQL Function: Complete Guide
September 10, 2025 - Let’s explore everything you need to know about the JSON_EXTRACT MySQL function, a powerful tool for retrieving data from JSON documents
🌐
MySQL
dev.mysql.com › doc › refman › 8.4 › en › json-functions.html
MySQL :: MySQL 8.4 Reference Manual :: 14.17 JSON Functions
Arguments parsed as JSON are indicated by json_doc; arguments indicated by val are not parsed. Functions that return JSON values always perform normalization of these values (see Normalization, Merging, and Autowrapping of JSON Values), and thus orders them.
Top answer
1 of 6
19

Took a bit of figuring out (more used to PostgreSQL where things are much easier!), but if you consult the fine manual here, under 12.16.2 Functions That Create JSON Values, there's the JSON_ARRAY function, but it's not much use really - at least in this case!

To answer the question "select and it return JSON", there are two ways of doing this, both rather painful!

You can either

  • use a "hack" - see the db-fiddle here,

  • or use one of the new MySQL supplied JSON functions here - which, ironically, appears to be even more of a hack than the hack itself! Only with MySQL! :-) (fiddle here).

Both answers use the MySQL GROUP_CONCAT function - this post helped. You might want to set the group_concat_max_len system variable to a bit more than its default (a paltry 1024)!

The first query is, as you can imagine, messy (DDL and DML at the bottom of this answer):

SELECT CONCAT('[', better_result, ']') AS best_result FROM
(
SELECT GROUP_CONCAT('{', my_json, '}' SEPARATOR ',') AS better_result FROM
(
  SELECT 
    CONCAT
    (
      '"name_field":'   , '"', name_field   , '"', ',' 
      '"address_field":', '"', address_field, '"', ','
      '"contact_age":'  , contact_age
    ) AS my_json
  FROM contact
) AS more_json
) AS yet_more_json;

Result:

[{"name_field":"Mary","address_field":"address one","contact_age":25},{"name_field":"Fred","address_field":"address two","contact_age":35},{"name_field":"Bill","address_field":"address three","contact_age":47}]

which is correct, but, let's face it, a bit of a nightmare!

Then there's the MySQL JSON_ARRAY() approach (which is even messier - thanks MySQL for your (non-existent) implementation of the TRANSLATE() function!).

SELECT
CONCAT
('[', REPLACE
  (
    REPLACE
    (
      GROUP_CONCAT
      (
        JSON_ARRAY
        (
          'name_field:', name_field, 
          'address_field:', address_field, 
          'age_field:', contact_age
        ) SEPARATOR ','
      ), '[', '{'
    ), ']', '}'
  ), ']'
) 
AS best_result2 
FROM contact

Same result!

====  TABLE CREATION and INSERT DDL and DML ============

    CREATE TABLE contact
    (
         name_field VARCHAR  (5) NOT NULL,
      address_field VARCHAR (20) NOT NULL,
      contact_age   INTEGER      NOT NULL
    );

    INSERT INTO contact
    VALUES
    ('Mary', 'address one',   25),
    ('Fred', 'address two',   35),
    ('Bill', 'address three', 47);
2 of 6
29

Converting a row to json

It doesn't sound to me like you want to aggregate JSON. You state you want the equivalent of row_to_json, if so then I suggest checking out the much simpler JSON_OBJECT

SELECT JSON_OBJECT(
  'name_field', name_field,
  'address_field', address_field,
  'contact_age', contact_age
)
FROM contact;

Aggregating JSON

As a side note, if you do need to aggregate a resultset to json. then the upcoming MySQL 8 will do that for you.

  • JSON_ARRAYAGG() Return result set as a single JSON array
  • JSON_OBJECTAGG() Return result set as a single JSON object
Find elsewhere
Top answer
1 of 1
1

I tested your functions in MySQL 8.2. They both work as you intend, creating a JSON object with a nested JSON object.

So you must be using MariaDB, not MySQL. I tested the function in MariaDB 11.2, and I can reproduce the unwanted behavior you describe, the JSON variable is converted to a string.

Please be aware that MariaDB and MySQL are effectively different products. In particular, they both implemented JSON features after MariaDB forked, so they behave differently. MySQL implemented a full-blown data type for JSON, but MariaDB basically implemented it as little more than an alias for the TEXT type with a few extra functions.

I was going to suggest using JSON_OBJECT('obj', CAST(s AS JSON)) which is a way to convert a string to JSON if the string contains value JSON syntax. That works in MySQL.

But MariaDB doesn't implement CAST(... AS JSON).

I found this workaround:

CopyCREATE FUNCTION sf_textjson()
RETURNs json
BEGIN
declare s JSON;
SET s = JSON_OBJECT('year', 2024);
RETURN JSON_OBJECT('obj', JSON_EXTRACT(s, '$'));
END 

I tested this on MariaDB 11.2, and it works as you intend:

Copymysql> SELECT VERSION();
+---------------------------------------+
| VERSION()                             |
+---------------------------------------+
| 11.2.2-MariaDB-1:11.2.2+maria~ubu2204 |
+---------------------------------------+

mysql> SELECT JSON_PRETTY( sf_textjson() ) AS _result;
+--------------------------------------------------+
| _result                                          |
+--------------------------------------------------+
| {
    "obj": 
    {
        "year": 2024
    }
} |
+--------------------------------------------------+

MariaDB is not MySQL. We should all stop thinking of it as a "drop-in replacement for MySQL," no matter what their marketing claims. It already has numerous features that are incompatible, and this list is growing larger over time.

🌐
GeeksforGeeks
geeksforgeeks.org › mysql › mysql-json-functions
MySQL JSON Functions - GeeksforGeeks
July 23, 2025 - JSON functions in MySQL allow working with JSON data in the database and retrieve JSON data from it.
🌐
MySQL
dev.mysql.com › doc › refman › 9.6 › en › json.html
MySQL :: MySQL 9.6 Reference Manual :: 13.5 The JSON Data Type
In MySQL 9.6, the optimizer can ... column being updated was declared as JSON. The UPDATE statement uses any of the three functions JSON_SET(), JSON_REPLACE(), or JSON_REMOVE() to update the column....
🌐
Percona
docs.percona.com › percona-server › 8.4 › json-overview.html
Percona Server for MySQL - JSON in Percona Server for MySQL
CREATE TABLE users ( id INT ... name column stores the user’s name. The info column stores JSON data using the JSON_OBJECT function......
🌐
SQLite
sqlite.org › json1.html
JSON Functions And Operators
The difference between MySQL json_extract() and SQLite json_extract() really only stands out when accessing individual values within the JSON that are strings or NULLs. The following table demonstrates the difference: The jsonb_extract() function works the same as the json_extract() function, ...
🌐
DataCamp
datacamp.com › doc › mysql › mysql-json-array
MySQL JSON_ARRAY() Function: Usage & Examples
Learn how to use MySQL's `JSON_ARRAY()` function to create JSON arrays from diverse data types, enhancing data manipulation and storage efficiency in your database applications.
🌐
DEV Community
dev.to › gbhorwood › mysql-using-json-data-and-not-hating-it-6lc
mysql: using json data and not hating it - DEV Community
May 29, 2024 - mysql also provides the -> operator as a shorthand for JSON_EXTRACT to help make our queries a little cleaner. SELECT some_json -> '$.artist' as artist FROM some_data WHERE id = 1; +--------------+ | artist | +--------------+ | "Bratmobile" | +--------------+ an annoyance we notice immediately with these results is that they're quote-enclosed. we don't want this. nobody wants this. let's remove them with the function ...
🌐
MySQL
dev.mysql.com › blog-archive › new-json-functions-in-mysql-5-7-22
MySQL :: New JSON functions in MySQL 5.7.22
MySQL 5.7 came with a function called JSON_MERGE, which merged two JSON documents into one. The merged document would contain the union of the members of the two documents. For the members that existed in both of the input documents, the merged document would contain an array of the two original ...
🌐
SitePoint
sitepoint.com › blog › databases › how to use json data fields in mysql databases
How to Use JSON Data Fields in MySQL Databases — SitePoint
December 31, 2024 - Whole JSON documents can be passed in INSERT or UPDATE statements, making it easy to move JSON to MySQL for storage and manipulation. For example, our book tags can be passed as an array (inside a string): INSERT INTO `book` (`title`, `tags`) VALUES ( 'ECMAScript 2015: A SitePoint Anthology', '["JavaScript", "ES2015", "JSON"]' ); ... JSON_ARRAY() function, which creates arrays.
🌐
Mydbops
mydbops.com › blog › 10-new-functions-for-json-columns-in-mysql-8
10 New JSON Functions in MySQL 8: Features and | Mydbops
2 weeks ago - Discover 10 powerful JSON functions introduced in MySQL 8, including JSON_TABLE(), JSON_ARRAYAGG(), JSON_VALUE(), and more. Learn how they enhance JSON...
🌐
MySQL
dev.mysql.com › blog-archive › mysql-5-7-lab-release-json-functions-part-2-querying-json-data
MySQL :: JSON Labs Release: JSON Functions, Part 2 -- Querying JSON Data
The current set of JSON types in MySQL is a super set of the types in the ECMA standard for JSON. We currently support NULL, DECIMAL, INTEGER, UNSIGNED INTEGER, DOUBLE, STRING, OBJECT, ARRAY, BOOLEAN, DATE, TIME, DATETIME, TIMESTAMP and OPAQUE (raw bits). When encountering some data, presumably ...
🌐
MySQL
dev.mysql.com › doc › refman › 8.4 › en › json-attribute-functions.html
MySQL :: MySQL 8.4 Reference Manual :: 14.17.5 Functions That Return JSON Value Attributes
An empty array, empty object, or scalar value has depth 1. A nonempty array containing only elements of depth 1 or nonempty object containing only member values of depth 1 has depth 2. Otherwise, a JSON document has depth greater than 2. mysql> SELECT JSON_DEPTH('{}'), JSON_DEPTH('[]'), JSON_DEPTH('true'); +------------------+------------------+--------------------+ | JSON_DEPTH('{}') | JSON_DEPTH('[]') | JSON_DEPTH('true') | +------------------+------------------+--------------------+ | 1 | 1 | 1 | +------------------+------------------+--------------------+ mysql> SELECT JSON_DEPTH('[10, 20]