JSON to Zod Schema — Java source
Generate Zod validation schemas from JSON. Infers z.string, z.number, z.boolean, z.object, z.array, z.null, and z.union for mixed arrays.
This is the Java implementation — the same logic the interactive tool runs, in a shareable, citable form.
// JSON -> Zod schema inference — Java (17+) port of the json-to-zod tool:
// the recursive InferZod walk from jsonToZod.ts / go.go. Object nodes are
// LinkedHashMap so textual key order survives; parse-shaped failures are
// returned, never thrown, mirroring the TS Result.
import java.util.*;
public final class JsonToZod {
@SuppressWarnings("unchecked")
static String infer(Object value, int indent) {
if (value == null) return "z.null()";
if (value instanceof Boolean) return "z.boolean()"; // Boolean, before Number
if (value instanceof Number) return "z.number()";
if (value instanceof String) return "z.string()";
if (value instanceof List) {
List<Object> arr = (List<Object>) value;
if (arr.isEmpty()) return "z.array(z.unknown())";
List<String> types = new ArrayList<>();
for (Object e : arr) types.add(infer(e, indent + 2));
boolean same = true; // one shared element type
for (String t : types) if (!t.equals(types.get(0))) same = false;
if (same) return "z.array(" + types.get(0) + ")";
String pad2 = " ".repeat(indent + 2); // union of EVERY element
StringBuilder u = new StringBuilder("z.array(z.union([\n");
for (String line : String.join(",\n", types).split("\n", -1))
u.append(line.isEmpty() ? "" : pad2).append(line).append('\n');
return u + "]))"; // close unpadded — pad("", indent) is "" in TS
}
Map<String, Object> obj = (Map<String, Object>) value;
if (obj.isEmpty()) return "z.object({})";
String pad2 = " ".repeat(indent + 2); // fields in textual order
StringBuilder out = new StringBuilder("z.object({\n");
for (String key : obj.keySet())
out.append(pad2).append(key).append(": ")
.append(infer(obj.get(key), indent + 2)).append(",\n");
return out + " ".repeat(indent) + "})";
}
// Reduce an arbitrary string to a usable JS identifier ("9lives" -> "_lives").
static String sanitizeVarName(String name) {
StringBuilder b = new StringBuilder();
for (char c : name.toCharArray())
if (Character.isLetterOrDigit(c) || c == '_' || c == '$') b.append(c);
String cleaned = b.toString();
int lead = 0;
while (lead < cleaned.length() && Character.isDigit(cleaned.charAt(lead))) lead++;
cleaned = (lead > 0 ? "_".repeat(lead) : "") + cleaned.substring(lead);
return cleaned.isEmpty() ? "schema" : cleaned;
}
static Map<String, Object> convert(Object tree, String rootName) {
return Map.of("ok", true, "code",
"const " + sanitizeVarName(rootName) + " = " + infer(tree, 2) + ";");
}
public static void main(String[] args) {
// {"name": "Ada", "admin": true, "meta": {"roles": ["admin", 1]}}
Map<String, Object> meta = new LinkedHashMap<>();
meta.put("roles", List.of("admin", 1));
Map<String, Object> doc = new LinkedHashMap<>();
doc.put("name", "Ada"); doc.put("admin", true); doc.put("meta", meta);
System.out.println(convert(doc, "User").get("code"));
}
}
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 →