glideapps / quicktype

Generate types and converters from JSON, Schema, and GraphQL
https://app.quicktype.io
Apache License 2.0
12.39k stars 1.08k forks source link

How to ignore unwanted `"$schema"` property in the generated code? #2413

Closed mean-ui-thread closed 5 months ago

mean-ui-thread commented 1 year ago

My problem originated when I wanted to use a "$schema" in my JSON data, which gives Visual Studio Code users a better quality of life: Each fields could emit a pop-up describing what it is when "description" is present in the schema file, and also provides improved auto-completion, highlights errors including when required fields are missing, or if there is a typo, or using an enum value that isn't valid, or failing a string pattern or number ranges, etc etc

{
  "$schema": "./PersonSchema.json",
  "firstName": "Mean",
  "middleName: "UI",
  "lastName": "Thread",
  "age": 42
}

The problem is, I want to enforce my schema as strictly as possible, not allowing unknown properties, so I am setting "additionalProperties" to false. Unfortunately, the generated Convert(json: string) function will throw an exception because of the unknown "$schema" field found in my json data.

This forces me to add a $schema property in my PersonSchema.json :

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$ref": "#/definitions/Person",
  "definitions": {
    "Person": {
      "type": "object",
      "additionalProperties": false, // <--------------- IMPORTANT TO ME
      "properties": {
        "$schema": {  "type": "string" },  // <-------------------------- HERE
        "firstName": {  "type": "string" },
        "middleName": {  "type": "string" },
        "lastName": {  "type": "string" },
        "age": { "type": "number" }
      }
    }
  }
}

which ends up code-generating this property in typescript for example:

export interface Person {
    schema?: string; // <--------------------------- HERE
    firstName?: string;
    middleName?: string;
    lastName?: string;
    age?: number;
}

Is there a way to skip $schema from being code-generated in the typescript interface without setting "additionalProperties" to true?

Ultimately, I would like this to fail:

{
  "$schema": "./PersonSchema.json",
  "firstName": "Mean",
  "middleName: "UI",
  "lastName": "Thread",
  "age": 42,
  "color": "blue"  // <----------------- this random data field that isn't part of the schema should not be allowed and should fail
}

And I would like my interface to look like this without any "$schema" properties:

export interface Person {
    firstName?: string;
    middleName?: string;
    lastName?: string;
    age?: number;
}

Thanks!

mean-ui-thread commented 1 year ago

I'm wondering if ignoring every data fields in the json data file that starts with $ would be a viable way to solve this, as those fields are typically used for metadata ($schema, $id, $ref, etc), not an actual data field... :thinking:

EDIT: This could be somewhat related: https://github.com/glideapps/quicktype/issues/964

In case "$schema" is something that most people would want to have part of their generated interface, perhaps we could have a --[no-]schema-field option?

feefladder commented 1 year ago

Note that language JSON and language JSON-schema schema are different. If you are using a JSON-schema, use quicktype -s schema aka quicktype --src-lang schema. This does not include the $schema

mean-ui-thread commented 1 year ago

@feefladder thank you for your reply. Unfortunately, I am using --src-lang schema, and I don't think you quite understand what I am saying.

First of all, this works just fine:

PersonSchema.json:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$ref": "#/definitions/Person",
  "definitions": {
    "Person": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "firstName": {  "type": "string" },
        "middleName": {  "type": "string" },
        "lastName": {  "type": "string" },
        "age": { "type": "number" }
      }
    }
  }
}

Which generates Person.ts just fine:

export interface Person {
    firstName?: string;
    middleName?: string;
    lastName?: string;
    age?: number;
}

and can open this data json file just fine:

{
  "firstName": "Mean",
  "middleName: "UI",
  "lastName": "Thread",
  "age": 42
}

Everything works fine the way it should be.

Problem starts occurring when I want to add a schema line to the data json file:

{
  "$schema": "./PersonSchema.json", // <----------- NEED THIS LINE FOR VSCODE
  "firstName": "Mean",
  "middleName: "UI",
  "lastName": "Thread",
  "age": 42
}

And then I get this exception when I try to open it: Invalid value for key "$schema" on Person. Expected boolean but got "./PersonSchema.json"

And the only way I solved this was to add "$schema": { "type": "string" }, to my schema file but it adds a schema property to my Person.ts and I don't want that.

I hope it makes sense...

mean-ui-thread commented 1 year ago

In case you're wondering why I want to add a schema line to the data json file, it is for visual studio code. Huge quality of life improvement when manually hand-writing these json data files:

Screenshot from 2023-09-26 09-24-29

ceeeeej commented 9 months ago

In case you're wondering why I want to add a schema line to the data json file, it is for visual studio code. Huge quality of life improvement when manually hand-writing these json data files:

Screenshot from 2023-09-26 09-24-29

Maybe you can try something like this in your .vscode/settings.json file for the project:

{
    "json.schemas": [
        {
            "fileMatch": [
                "/*.json"
            ],
            "url": "myschema.jsonschema"
        }
    ]
}
inferrinizzard commented 5 months ago

This looks like too niche a use case, I would recommend the suggestion from @ceeeeej to configure your VSCode to provide the JSON Schema Intellisense without explicitly including the $schema property.

Or if you really wish to remove specific strings from your quicktype output, you can also do so by extending with a custom Renderer class