JSON to SQL INSERT — Java source
Convert a JSON array of objects into SQL INSERT statements. Properly escapes strings, handles nulls, booleans, numbers, nested objects, and multi-row inserts.
This is the Java implementation — the same logic the interactive tool runs, in a shareable, citable form.
// json-to-sql — pure JSON → SQL INSERT converter. Java port (canonical TS:
// src/lib/jsonToSql.ts; Go twin: cli/json-to-sql). The JVM stdlib has no JSON
// codec, so — like the Rust port — this carries a small hand-rolled JSON DOM
// parser: LinkedHashMap keeps first-seen key order and number tokens stay raw
// text, so column unions and literals match the TS reference. Errors return
// through the Result record — never thrown on user input.
import java.util.*;
public final class JsonToSql {
record Num(String raw) {} // raw number token — renders like JS String(n)
record Opts(String table, String dialect, boolean quoteIdentifiers) {
Opts(String t) { this(t, "standard", true); } }
record Result(boolean ok, String sql, int rows, String error) {}
// ---- tiny JSON parser: null | Boolean | Num | String | List | LinkedHashMap ----
static final class P {
final String s; int i;
P(String s) { this.s = s; }
RuntimeException bad() { return new IllegalArgumentException("Invalid JSON"); }
Object parse() { Object v = value(); ws(); if (i != s.length()) throw bad(); return v; }
void ws() { while (i < s.length() && Character.isWhitespace(s.charAt(i))) i++; }
char peek() { return i < s.length() ? s.charAt(i) : '\0'; }
char take() { if (i >= s.length()) throw bad(); return s.charAt(i++); }
Object value() {
ws(); char c = peek();
if (c == '{') { i++; LinkedHashMap<String, Object> m = new LinkedHashMap<>(); ws();
if (peek() == '}') { i++; return m; }
while (true) { ws(); String k = (String) value(); ws(); if (take() != ':') throw bad();
m.put(k, value()); ws(); char d = take(); if (d == '}') return m; if (d != ',') throw bad(); } }
if (c == '[') { i++; List<Object> l = new ArrayList<>(); ws();
if (peek() == ']') { i++; return l; }
while (true) { l.add(value()); ws(); char d = take(); if (d == ']') return l; if (d != ',') throw bad(); } }
if (c == '"') return str();
if (s.startsWith("null", i)) { i += 4; return null; }
if (s.startsWith("true", i)) { i += 4; return Boolean.TRUE; }
if (s.startsWith("false", i)) { i += 5; return Boolean.FALSE; }
int from = i;
while ("-+.eE0123456789".indexOf(peek()) >= 0 && peek() != '\0') i++;
if (i == from) throw bad();
return new Num(s.substring(from, i));
}
String str() {
StringBuilder b = new StringBuilder();
if (take() != '"') throw bad();
while (true) { char c = take();
if (c == '"') return b.toString();
if (c == '\\') { char e = take();
b.append(switch (e) {
case '"', '\\', '/' -> e; case 'b' -> '\b'; case 'f' -> '\f';
case 'n' -> '\n'; case 'r' -> '\r'; case 't' -> '\t';
case 'u' -> { int cp = 0; for (int k = 0; k < 4; k++) cp = cp * 16 + Character.digit(take(), 16); yield (char) cp; }
default -> throw bad(); }); }
else b.append(c); }
}
}
// ---- SQL rendering ----
static String escapeSqlString(String s, String d) {
String o = s.replace("'", "''");
if (d.equals("mysql")) o = o.replace("\\", "\\\\").replace("\0", "\\0")
.replace("\n", "\\n").replace("\r", "\\r").replace("\u001a", "\\Z");
return o;
}
static String sqlLiteral(Object v, String d) {
if (v == null) return "NULL";
if (v instanceof Boolean b) return d.equals("mysql") ? (b ? "1" : "0") : (b ? "TRUE" : "FALSE");
if (v instanceof Num n) return n.raw();
if (v instanceof String s) return "'" + escapeSqlString(s, d) + "'";
return "'" + escapeSqlString(compact(v), d) + "'"; // List/Map → JSON text
}
static String compact(Object v) { // JSON.stringify-style re-emission
if (v == null) return "null";
if (v instanceof Boolean b) return b.toString();
if (v instanceof Num n) return n.raw();
if (v instanceof String s) return "\"" + s.replace("\\", "\\\\").replace("\"", "\\\"")
.replace("\n", "\\n").replace("\r", "\\r").replace("\t", "\\t") + "\"";
if (v instanceof List<?> l) { StringJoiner j = new StringJoiner(",", "[", "]");
for (Object x : l) j.add(compact(x)); return j.toString(); }
@SuppressWarnings("unchecked") Map<String, Object> m = (Map<String, Object>) v;
StringJoiner j = new StringJoiner(",", "{", "}");
for (Map.Entry<String, Object> e : m.entrySet()) j.add(compact(e.getKey()) + ":" + compact(e.getValue()));
return j.toString();
}
static String quoteIdent(String n, String d, boolean q) { return !q ? n : d.equals("mysql") ? "`" + n + "`" : "\"" + n + "\""; }
static String sanitizeIdent(String n) { String c = n.replaceAll("[^A-Za-z0-9_]", "_"); return c.isEmpty() ? "tbl" : c; }
/** Convert a JSON document into a single multi-row INSERT. A bare object is
* one row; an array is many. Heterogeneous rows unify via first-seen columns. */
static Result jsonToInsert(String json, Opts opts) {
Object data;
try { data = new P(json).parse(); }
catch (IllegalArgumentException e) { return new Result(false, "", 0, e.getMessage()); }
List<Object> rows = data instanceof List<?> l ? new ArrayList<>(l) : new ArrayList<>(Collections.singletonList(data));
if (rows.isEmpty()) return new Result(false, "", 0, "No rows to insert.");
for (Object r : rows) if (!(r instanceof LinkedHashMap)) return new Result(false, "", 0, "Rows must be objects.");
String d = opts.dialect(); boolean q = opts.quoteIdentifiers();
String table = quoteIdent(sanitizeIdent(opts.table()), d, q);
List<String> cols = new ArrayList<>(); // first-seen union of every row's keys
for (Object r : rows) for (String k : ((Map<String, Object>) r).keySet()) if (!cols.contains(k)) cols.add(k);
StringBuilder sql = new StringBuilder("INSERT INTO ").append(table).append(" (")
.append(String.join(", ", cols.stream().map(c -> quoteIdent(c, d, q)).toList())).append(") VALUES\n");
for (int r = 0; r < rows.size(); r++) {
Map<String, Object> row = (Map<String, Object>) rows.get(r);
StringJoiner vals = new StringJoiner(", ");
for (String c : cols) vals.add(sqlLiteral(row.get(c), d)); // absent → null → NULL
sql.append(" (").append(vals).append(")").append(r == rows.size() - 1 ? ";" : ",\n");
}
return new Result(true, sql.toString(), rows.size(), null);
}
public static void main(String[] args) {
System.out.println(jsonToInsert("[{\"id\":1,\"name\":\"O'Hara\"},{\"id\":2,\"age\":30.5,\"tags\":[\"a\",\"b\"]}]",
new Opts("users")).sql()); // INSERT INTO "users" ("id", "name", "age", "tags") VALUES ...
}
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →