Character-level parserMultiple statementsNo SQL executed

SQL INSERT to JSON Converter

Paste SQL INSERT statements and get JSON rows back. Values containing commas, brackets and escaped quotes are parsed correctly, and nothing is executed. All processing happens locally in your browser.

INSERT statements only

Turn SQL INSERT statements into JSON rows

The statement is scanned character by character rather than with regular expressions, so commas, parentheses and doubled quotes inside string values are handled correctly. No SQL is executed and no database is contacted.

Tables1
Rows3
Columns4
Statements1
InputSQL INSERT statements
OutputJSON
[
  {
    "id": 1,
    "name": "Ada Lovelace",
    "email": "ada@example.com",
    "is_active": true
  },
  {
    "id": 2,
    "name": "Alan Turing",
    "email": "alan@example.com",
    "is_active": true
  },
  {
    "id": 3,
    "name": "Grace Hopper",
    "email": "grace@example.com",
    "is_active": false
  }
]
Conversion options

Parsed 1 statement into 3 rows across 1 table. Nothing was executed.

Row previewusers — first 3 of 3
Preview of rows parsed from users
idnameemailis_active
1Ada Lovelaceada@example.comtrue
2Alan Turingalan@example.comtrue
3Grace Hoppergrace@example.comfalse
Load sampleLoad a statement

What is in scope: INSERT statements, not queries

This tool reads SQL INSERT statements and turns their VALUES tuples into JSON objects. That is the whole scope, and it is worth being precise about because "SQL to JSON" is an ambiguous phrase.

Input Supported? Why
INSERT INTO t (a, b) VALUES (1, 'x'); Yes The statement contains the data itself
Several INSERT statements at once Yes Each is parsed and can be grouped by table
INSERT INTO t SET a = 1, b = 'x'; Yes MySQL's alternative single-row form
SELECT * FROM users; No A SELECT describes data to fetch. There is no data in the text to convert.
CREATE TABLE users (...); No Defines a schema, contains no rows
INSERT INTO a SELECT * FROM b; No The rows come from another table, not from literal values

No SQL is executed. This page does not connect to a database, open a network connection, or evaluate any statement. Your SQL is read as text. That also means an expression such as NOW() cannot be resolved to a value — there is no database to ask.

How to convert INSERT statements

  1. Paste one or more statements Line comments (--, #) and block comments (/* … */) are stripped, so a statement copied straight from a dump file or migration works as-is.
  2. Choose the output shape A flat array of every row, or an object keyed by table name when the input touches more than one table.
  3. Check the row preview The first ten rows are shown as a table so you can confirm the columns lined up before trusting the JSON.
  4. Copy or download The download filename is taken from the table name in the statement.

Why a character-level parser rather than a regular expression

The obvious approach to this problem is a regex that finds bracketed groups and splits them on commas. It fails on real data almost immediately, and the failures are silent — you get JSON out, but it is wrong.

Three cases break it:

-- A comma inside a string value
INSERT INTO people (name) VALUES ('Smith, John');
-- Naive split on "," produces two columns from one value.

-- A closing bracket inside a string value
INSERT INTO companies (name) VALUES ('Acme (UK) Ltd');
-- Matching /\(([^)]+)\)/ stops at the bracket inside the string.

-- A doubled quote, the SQL way of escaping an apostrophe
INSERT INTO people (name) VALUES ('O''Brien');
-- Treating the second quote as a terminator truncates the value to "O".

The parser here walks the statement one character at a time, tracking whether it is inside a quoted string and how deep it is in nested brackets. A comma only separates values at bracket depth zero and outside a string. That is the same approach a real SQL lexer takes, and it is the reason those three cases come out correctly.

Both '' (the SQL standard) and \' (the MySQL extension) are recognised as escaped quotes.

Examples: how SQL values map to JSON types

Input:

INSERT INTO orders (id, total, discount, note, created_at, paid) VALUES
  (1001, 129.99, NULL, 'Gift wrap', NOW(), TRUE);

Output:

[
  {
    "id": 1001,
    "total": 129.99,
    "discount": null,
    "note": "Gift wrap",
    "created_at": "NOW()",
    "paid": true
  }
]
SQL literal JSON value Note
1001 1001 Integer
129.99 129.99 Number, or a string if you enable the decimal option
'Gift wrap' "Gift wrap" Quotes removed, escapes resolved
NULL null Case-insensitive
TRUE / FALSE true / false JSON booleans
NOW() "NOW()" Kept as a string; there is no database to evaluate it
DEFAULT null The real default lives in the table definition
X'4A2B' "X'4A2B'" Binary literal, kept verbatim
9007199254740993 "9007199254740993" Beyond JavaScript's exact integer range, so kept as a string

That last row matters for real database exports. JavaScript numbers lose precision above 253−1, so a 64-bit ID such as a Snowflake identifier would silently change value if converted to a number. It is kept as a string instead, and a warning says so.

Decimals and money

By default 129.99 becomes the number 129.99. That is convenient but it is a binary float, so 0.1 + 0.2 style rounding applies downstream. When the values are currency and will be summed, switch on "Keep decimals as strings" so the exact digits survive.

Column names and identifier quoting

Identifiers are unquoted before being used as JSON keys, across all three conventions:

MySQL:      `user_id`     →  user_id
PostgreSQL: "user_id"     →  user_id
SQL Server: [user_id]     →  user_id

Schema-qualified table names are preserved, so public.users stays public.users in the grouped output.

An INSERT with no column list is a special case:

INSERT INTO users VALUES (1, 'Ada', TRUE);

The column names are not in the statement — they live in the table definition. Keys are generated as column_1, column_2, column_3, and a warning explains why. Rename them, or paste a version of the statement that names its columns.

When a column list is present, the value count is checked against it row by row. A mismatch is reported with the row number and both counts, because a single missing comma in a long tuple otherwise shifts every value silently.

Use cases

  • Building test fixtures from real seed data. A migration's INSERT block becomes a JSON fixture for unit tests without retyping it.
  • Mocking an API from a database dump. Turn a table's rows into a JSON array that a mock server or MSW handler can return.
  • Moving relational rows into a document store. A first pass at the shape before adding nesting by hand.
  • Reviewing a migration you did not write. A table preview of the parsed rows is far easier to check than a wall of tuples.
  • Populating a spreadsheet. Convert to JSON here, then to columns with JSON to CSV.
  • Finding a bad row in a failing import. The value-count check names the row and the mismatch, which is usually the actual bug.

Going the other direction, or working with different formats: CSV to JSON handles spreadsheet exports, SQL Formatter tidies the statement itself, and JSON to YAML takes the result further.

Privacy

Parsing runs entirely in your browser. The SQL is not uploaded, not stored, and not written to browser storage — it exists only in the page while the tab is open.

Analytics: only the output shape, a coarse row-count band and the number of tables are recorded. No SQL text, table name, column name or value is ever included.

Frequently Asked Questions

Can I paste a SELECT query?

No, and the tool says so explicitly rather than returning empty output. A SELECT describes rows to fetch from a database; the text contains no data. To get JSON from a query, run it in your client and export the results, or export them as INSERT statements.

Does this connect to my database?

No. There is no connection, no credential field and no network request. The statement is read as text in your browser, which is also why a function such as NOW() cannot be resolved — there is no server to evaluate it against.

Why did my value with a comma in it get parsed correctly here but not elsewhere?

Because the parser tracks quoting and bracket depth character by character instead of splitting on commas. A comma only separates values when it is outside a string and at bracket depth zero, so 'Smith, John' and 'Acme (UK) Ltd' stay intact.

What happens to NOW() or CURRENT_TIMESTAMP?

They are kept verbatim as strings, and a warning lists them. Evaluating them would require a database connection and a server clock, so inventing a timestamp would be worse than being explicit. Replace them with literal values before converting if you need real dates.

Why are my columns named column_1 and column_2?

Because the INSERT statement did not include a column list, and the real names live in the table definition rather than the statement. Either add the column names to the SQL, or rename the keys afterwards.

Why is my large ID a string instead of a number?

Any integer above 9007199254740991 cannot be represented exactly by a JavaScript number, so converting it would silently change the value. Those are kept as strings to preserve every digit, with a warning saying which value triggered it.

Should I convert decimal money values to numbers?

Usually not, if the values will be added up later. JSON numbers are binary floats, so repeated arithmetic on currency introduces rounding error. The "keep decimals as strings" option preserves the exact digits from the SQL for that case.