Input Format: JSON Schema formatted as YAML
Output Language: JSON Schema formatted as JSON, as well as typescript-zod
CLI, npm, or app.quicktype.io: npm package CLI
Version: 23.0.115
Description
Obviously when one adds additional attributes to a schema entry, those attributes should be reflected in the generated code.
Because they're not reflected in generated code, I'd have to re-edit that code to have the attributes.
Input Data
Sample JSON Schema in YAML format
$schema: "http://json-schema.org/draft-06/schema#"
$id: "./bad-schema.schema.json"
title: "Station"
description: "A product in the catalog"
type: "object"
properties:
simpleString:
description: |
Simple string has no problem
type: string
pattern:
description: |
String with a pattern,
the pattern goes through
type: string
pattern: '^[0-9][0-9][0-9][0-9][0-9]$'
zipCode:
description: |
String with min/masLength, hence
with numerical attributes, and
a pattern, only the pattern goes
through.
type: string
maxLength: 5
minLength: 5
pattern: '^[0-9][0-9][0-9][0-9][0-9]$'
minMaxLength:
type: string
maxLength: 5
minLength: 5
simpleNumber:
description: |
Simple number no problem
type: number
percent:
description: |
Attributes on number do not
go through
type: number
minimum: 0
maximum: 1
percentage:
description: |
Attributes on number do not
go through
type: number
minimum: 0
maximum: 100
enumSchema:
type: string
enum:
- enum1
- enum2
- enum3
This demonstrates a few variants of the issue so you can see different angles.
Expected Behaviour / Output
The pattern I've found so far is that attributes with string values are generated into the output, while those with numerical output are not.
My first stage test is conversion of the YAML-formatted schema to JSON format because of course most software will want to see JSON formatted JSON schema's. But, the YAML format is easier to read and edit while being functionally equivalent.
In generated TypeScript the validation code simply does not reflect any attribute. And, in the generated type definition there are no generated TSDOC annotations.
In the generated TypeScript-Zod validator, no attributes are generated.
Also, I hand-edited a copy of the JSON formatted schema to have the desired attributes. No code generated from that schema has the desired attribute values.
This generates the following - which has no sign of the attributes.
// To parse this data:
//
// import { Convert, BadSchemaEditedTs } from "./file";
//
// const badSchemaEditedTs = Convert.toBadSchemaEditedTs(json);
//
// These functions will throw an error if the JSON doesn't
// match the expected interface, even if the JSON is valid.
/**
* A product in the catalog
*/
export interface BadSchemaEditedTs {
minMaxLength?: string;
/**
* String with a pattern,
* the pattern goes through
*/
pattern?: string;
/**
* Attributes on number do not
* go through
*/
percent?: number;
/**
* Attributes on number do not
* go through
*/
percentage?: number;
/**
* Simple number no problem
*/
simpleNumber?: number;
/**
* Simple string has no problem
*/
simpleString?: string;
/**
* String with min/masLength, hence
* with numerical attributes, and
* a pattern, only the pattern goes
* through.
*/
zipCode?: string;
[property: string]: any;
}
// Converts JSON strings to/from your types
// and asserts the results of JSON.parse at runtime
export class Convert {
public static toBadSchemaEditedTs(json: string): BadSchemaEditedTs {
return cast(JSON.parse(json), r("BadSchemaEditedTs"));
}
public static badSchemaEditedTsToJson(value: BadSchemaEditedTs): string {
return JSON.stringify(uncast(value, r("BadSchemaEditedTs")), null, 2);
}
}
function invalidValue(typ: any, val: any, key: any, parent: any = ''): never {
const prettyTyp = prettyTypeName(typ);
const parentText = parent ? ` on ${parent}` : '';
const keyText = key ? ` for key "${key}"` : '';
throw Error(`Invalid value${keyText}${parentText}. Expected ${prettyTyp} but got ${JSON.stringify(val)}`);
}
function prettyTypeName(typ: any): string {
if (Array.isArray(typ)) {
if (typ.length === 2 && typ[0] === undefined) {
return `an optional ${prettyTypeName(typ[1])}`;
} else {
return `one of [${typ.map(a => { return prettyTypeName(a); }).join(", ")}]`;
}
} else if (typeof typ === "object" && typ.literal !== undefined) {
return typ.literal;
} else {
return typeof typ;
}
}
function jsonToJSProps(typ: any): any {
if (typ.jsonToJS === undefined) {
const map: any = {};
typ.props.forEach((p: any) => map[p.json] = { key: p.js, typ: p.typ });
typ.jsonToJS = map;
}
return typ.jsonToJS;
}
function jsToJSONProps(typ: any): any {
if (typ.jsToJSON === undefined) {
const map: any = {};
typ.props.forEach((p: any) => map[p.js] = { key: p.json, typ: p.typ });
typ.jsToJSON = map;
}
return typ.jsToJSON;
}
function transform(val: any, typ: any, getProps: any, key: any = '', parent: any = ''): any {
function transformPrimitive(typ: string, val: any): any {
if (typeof typ === typeof val) return val;
return invalidValue(typ, val, key, parent);
}
function transformUnion(typs: any[], val: any): any {
// val must validate against one typ in typs
const l = typs.length;
for (let i = 0; i < l; i++) {
const typ = typs[i];
try {
return transform(val, typ, getProps);
} catch (_) {}
}
return invalidValue(typs, val, key, parent);
}
function transformEnum(cases: string[], val: any): any {
if (cases.indexOf(val) !== -1) return val;
return invalidValue(cases.map(a => { return l(a); }), val, key, parent);
}
function transformArray(typ: any, val: any): any {
// val must be an array with no invalid elements
if (!Array.isArray(val)) return invalidValue(l("array"), val, key, parent);
return val.map(el => transform(el, typ, getProps));
}
function transformDate(val: any): any {
if (val === null) {
return null;
}
const d = new Date(val);
if (isNaN(d.valueOf())) {
return invalidValue(l("Date"), val, key, parent);
}
return d;
}
function transformObject(props: { [k: string]: any }, additional: any, val: any): any {
if (val === null || typeof val !== "object" || Array.isArray(val)) {
return invalidValue(l(ref || "object"), val, key, parent);
}
const result: any = {};
Object.getOwnPropertyNames(props).forEach(key => {
const prop = props[key];
const v = Object.prototype.hasOwnProperty.call(val, key) ? val[key] : undefined;
result[prop.key] = transform(v, prop.typ, getProps, key, ref);
});
Object.getOwnPropertyNames(val).forEach(key => {
if (!Object.prototype.hasOwnProperty.call(props, key)) {
result[key] = transform(val[key], additional, getProps, key, ref);
}
});
return result;
}
if (typ === "any") return val;
if (typ === null) {
if (val === null) return val;
return invalidValue(typ, val, key, parent);
}
if (typ === false) return invalidValue(typ, val, key, parent);
let ref: any = undefined;
while (typeof typ === "object" && typ.ref !== undefined) {
ref = typ.ref;
typ = typeMap[typ.ref];
}
if (Array.isArray(typ)) return transformEnum(typ, val);
if (typeof typ === "object") {
return typ.hasOwnProperty("unionMembers") ? transformUnion(typ.unionMembers, val)
: typ.hasOwnProperty("arrayItems") ? transformArray(typ.arrayItems, val)
: typ.hasOwnProperty("props") ? transformObject(getProps(typ), typ.additional, val)
: invalidValue(typ, val, key, parent);
}
// Numbers can be parsed by Date but shouldn't be.
if (typ === Date && typeof val !== "number") return transformDate(val);
return transformPrimitive(typ, val);
}
function cast<T>(val: any, typ: any): T {
return transform(val, typ, jsonToJSProps);
}
function uncast<T>(val: T, typ: any): any {
return transform(val, typ, jsToJSONProps);
}
function l(typ: any) {
return { literal: typ };
}
function a(typ: any) {
return { arrayItems: typ };
}
function u(...typs: any[]) {
return { unionMembers: typs };
}
function o(props: any[], additional: any) {
return { props, additional };
}
function m(additional: any) {
return { props: [], additional };
}
function r(name: string) {
return { ref: name };
}
const typeMap: any = {
"BadSchemaEditedTs": o([
{ json: "minMaxLength", js: "minMaxLength", typ: u(undefined, "") },
{ json: "pattern", js: "pattern", typ: u(undefined, "") },
{ json: "percent", js: "percent", typ: u(undefined, 3.14) },
{ json: "percentage", js: "percentage", typ: u(undefined, 3.14) },
{ json: "simpleNumber", js: "simpleNumber", typ: u(undefined, 3.14) },
{ json: "simpleString", js: "simpleString", typ: u(undefined, "") },
{ json: "zipCode", js: "zipCode", typ: u(undefined, "") },
], "any"),
};
Desires
To use this with AJV for example the attributes must be in the JSON-schema.
For generated Zod validation code to be useful, it must have the attributes as part of the Zod schema.
Several tools exist for JavaScript/TypeScript that can perform validation derived from the JSDOC/TSDOC annotations. Hence it would be useful to output those annotations.
The generated TypeScript code looks like it wants to do validation but isn't there.
Issue Type
QuickType output -- It is related to an earlier report I made https://github.com/glideapps/quicktype/issues/2556
Context (Environment, Version, Language)
Input Format: JSON Schema formatted as YAML Output Language: JSON Schema formatted as JSON, as well as typescript-zod
CLI, npm, or app.quicktype.io: npm package CLI Version: 23.0.115
Description
Obviously when one adds additional attributes to a schema entry, those attributes should be reflected in the generated code.
Because they're not reflected in generated code, I'd have to re-edit that code to have the attributes.
Input Data
Sample JSON Schema in YAML format
This demonstrates a few variants of the issue so you can see different angles.
Expected Behaviour / Output
The pattern I've found so far is that attributes with string values are generated into the output, while those with numerical output are not.
My first stage test is conversion of the YAML-formatted schema to JSON format because of course most software will want to see JSON formatted JSON schema's. But, the YAML format is easier to read and edit while being functionally equivalent.
In generated TypeScript the validation code simply does not reflect any attribute. And, in the generated type definition there are no generated TSDOC annotations.
In the generated TypeScript-Zod validator, no attributes are generated.
Also, I hand-edited a copy of the JSON formatted schema to have the desired attributes. No code generated from that schema has the desired attribute values.
Steps to Reproduce
Using the above YAML-formatted schema ...
This produces:
Generate Zod validators:
This generates:
No attributes make it to the output.
Then, to disprove the idea that the problem is due to the YAML formatted schema, I hand edited the JSON formatted generated schema:
This made no difference in other generated code.
As for the generated TypeScript code, shouldn't the attributes show up there?
This generates the following - which has no sign of the attributes.
Desires
To use this with AJV for example the attributes must be in the JSON-schema.
For generated Zod validation code to be useful, it must have the attributes as part of the Zod schema.
Several tools exist for JavaScript/TypeScript that can perform validation derived from the JSDOC/TSDOC annotations. Hence it would be useful to output those annotations.
The generated TypeScript code looks like it wants to do validation but isn't there.