quicktype icon indicating copy to clipboard operation
quicktype copied to clipboard

Disable merging similar types, enforce type names from input

Open JulianGmp opened this issue 3 years ago • 6 comments

Hello,

I wanted to use json schemas and quicktype to generate code in two languages and I quickly noticed that the generated types do not necessarily match the schema I gave it.

To generate code I use the following command:

quicktype -s schema --lang Rust --out Output.rs --no-combine-classes schema.json

quicktype version 15.0.260

My json schema:

{
  "$schema": "http://json-schema.org/draft-07/schema",
  "$ref": "#/definitions/Datastructure1",

  "definitions": {
    "Datastructure1": {
      "type": "object",
      "properties": {
        "e1": {
          "$ref": "#/definitions/ThatFirstEnum"
        },
        "e2": {
          "$ref": "#/definitions/MyOtherEnum"
        }
      },
      "required": ["e1", "e2"]
    },
    "ThatFirstEnum": {
      "type": "string",
      "enum": [
        "hello", "world"
      ]
    },
    "MyOtherEnum": {
      "type": "string",
      "enum": [
        "hello", "world"
      ]
    }
  }
}

The generated Rust Code:

extern crate serde_derive;

#[derive(Serialize, Deserialize)]
pub struct Output {
    #[serde(rename = "e1")]
    e1: Enum,

    #[serde(rename = "e2")]
    e2: Enum,
}

#[derive(Serialize, Deserialize)]
pub enum Enum {
    #[serde(rename = "hello")]
    Hello,

    #[serde(rename = "world")]
    World,
}

The two enums I defined, ThatFirstEnum and MyOtherEnum, were summarized into one enum type called Enum, because they share the same values. Even though I passed the --no-combine-classes option.

I'm not sure how you feel about this, but in my opinion a code generator should keep the schema names as accurately as possible (within reason of course, depending on the target language). The automatic renaming should not even be the default behaviour in my opinion, but disabling it should be a working option. I've seen a few similar issues (#1578 #1638 #1641), but seems like they went unnoticed.

JulianGmp avatar Mar 13 '21 19:03 JulianGmp

I'm having a similar issue with C#. When generating code from a schema, the type hierarchy that is created by QuickType is arbitrary/random/misleading, and makes life unnecessarily difficult when trying to map types between the application and the de/serializing code.

(In case it helps others: I'm finding that in the case of C#, using dynamic/ExpandoObjects to bypass QuickType's generated types is helpful. Though at that point it's hard to say whether Quick Type is actually helpful or a distraction...)

hmijail avatar Sep 13 '21 07:09 hmijail

Same issue here, with typescript, any possibility to generate just the types, without merging similar ones?

greuze avatar Dec 01 '21 08:12 greuze

HI, we encounter the same problem with enums in golang.

Has anyone already had a look into this issue?

jens1205 avatar Jan 27 '22 08:01 jens1205

Having the same issue. We generate code for two different API versions, a few enums are the same between versions so they are getting merged and thus a dependency between the two API versions is introduced (the similar enums become shared).

Ex. EnumV1{VAL_1, VAL_2} and EnumV2{VAL_1, VAL_2} becomes just Enum{VAL_1, VAL_2} which is referenced by V1 and V2 entities.

cryon avatar May 23 '22 07:05 cryon

Same issue with TypeScript, though I'm not passing a JSONSchema, just raw json.

My input file has (abridged):

{
    "account_addresses": {
            "rows": {
                "row": {
                    "role": null,
                    "address": null,
                    "chain_link": null,
                    "balance": null
                },
                "with": {
                    "role": null,
                    "address": null,
                    "chain_link": null,
                    "balance": null
                }
            }
        }
}

The generated TS:

export interface AccountAddresses {
    rows:          AccountAddressesRows;
}

export interface AccountAddressesRows {
    row:  PurpleRow;
    with: PurpleRow;
}

export interface PurpleRow {
    role:      null;
    address:   null;
    chainLink: null;
    balance:   null;
}

When it should be

export interface AccountAddresses {
    rows:          AccountAddressesRows;
}

export interface AccountAddressesRows {
    row:  PurpleRow;
    with: PurpleWith;
}

export interface PurpleRow {
    role:      null;
    address:   null;
    chainLink: null;
    balance:   null;
}

export interface PurpleWith {
    role:      null;
    address:   null;
    chainLink: null;
    balance:   null;
}

FFdhorkin avatar Jul 12 '23 01:07 FFdhorkin

I just ran into a more problematic scenario. The following schema:

{
  "properties": {
    "deletedBy": {
      "enum": [
        "user",
        "api-key",
        "service"
      ],
      "type": "string"
    },
    "updatedBy": {
      "enum": [
        "user",
        "api-key",
        "service"
      ],
      "type": "string"
    }
  },
  "required": [],
  "type": "object"
}

Generates the following Typescript code:

export type MyType = {
    deletedBy?: TedBy;
    updatedBy?: TedBy;
    [property: string]: any;
}

export enum TedBy {
    APIKey = "api-key",
    Service = "service",
    User = "user",
}

Even though deletedBy and updatedBy have the same type in the schema, I expect:

  1. To have types with meaningful names. TedBy is very confusing
  2. To have two separate (but equal) types called DeletedBy and UpdatedBy

I suspect this behavior is related to this.

Even when I explicitly assign different titles to these schema objects, it ignores the second title and still combines them into one.

I think it should be possible to disable type deduplication and name combination especially for languages like Typescript that offer a Structural Type System.

fardjad avatar Nov 15 '23 12:11 fardjad