Consider this example:
import { z } from "zod";
const schema = z.object({
value: z.string().nullable().default(null),
});
console.log(JSON.stringify(z.toJSONSchema(schema), null, 2));
In Zod 4, this omits something like the following (trimmed for brevity):
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"value": {
"default": null,
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"value"
],
"additionalProperties": false
}
For JSON Schema targets, the equivalent compact form is:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"value": {
"default": null,
"type": ["string", "null"]
}
},
"required": [
"value"
],
"additionalProperties": false
}
Would you consider a conservative PR that would update toJSONSchema(...) to omit the more compact form when possible?
The proposed scope would be narrow:
- only JSON Schema targets that support type arrays (
draft-04, draft-07, draft-2020-12 - not openapi-3.0)
- only
anyOf where all branches have the form { type: ... }
- no compaction when branches contain constraints, metadata,
$ref, const, enum, etc.
- no change to
oneOf / exclusive unions
My motivation is that I have projects that generate JSON schemas and check them into version control, and we like having the simplest possible form of things for easier reading / review. We're looking to upgrade from v3 + https://github.com/StefanTerdell/zod-to-json-schema to v4. We love that Zod now has JSON Schema serialization built in, but are missing the compact form that zod-to-json-schema omits.
I'll acknowledge that we can do this in userspace as our own post-processing step, which we'll likely do if y'all don't want this in Zod itself. But I thought this could be something that others could appreciate too, so wanted to raise it for discussion. Thanks!
Consider this example:
In Zod 4, this omits something like the following (trimmed for brevity):
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "value": { "default": null, "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "value" ], "additionalProperties": false }For JSON Schema targets, the equivalent compact form is:
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "value": { "default": null, "type": ["string", "null"] } }, "required": [ "value" ], "additionalProperties": false }Would you consider a conservative PR that would update
toJSONSchema(...)to omit the more compact form when possible?The proposed scope would be narrow:
draft-04,draft-07,draft-2020-12- notopenapi-3.0)anyOfwhere all branches have the form{ type: ... }$ref,const,enum, etc.oneOf/ exclusive unionsMy motivation is that I have projects that generate JSON schemas and check them into version control, and we like having the simplest possible form of things for easier reading / review. We're looking to upgrade from v3 + https://github.com/StefanTerdell/zod-to-json-schema to v4. We love that Zod now has JSON Schema serialization built in, but are missing the compact form that
zod-to-json-schemaomits.I'll acknowledge that we can do this in userspace as our own post-processing step, which we'll likely do if y'all don't want this in Zod itself. But I thought this could be something that others could appreciate too, so wanted to raise it for discussion. Thanks!