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

JSON Schema generation differs between Python and other languages [Rust, Dart, Kotlin] #2417

Open elyoni opened 1 year ago

elyoni commented 1 year ago

I would like to report an issue concerning the generation of JSON Schema, which appears to differ between Python and other programming languages such as Rust, Dart, and Kotlin.

The problem arises when generating a Python class using the provided JSON Schema. In Python, the resulting variable types are unusable. For instance, the variable "parameters" is defined as a dictionary, where every value is expected to be None. However, in Rust (for example), the corresponding type is "JSON," not None.

Specifically, the issue lies in the type assigned to the dictionary values. In Python, they are consistently set to None, whereas in Rust, they are assigned the type "JSON." This inconsistency causes problems when using the "from_dict" function, as it checks that every value in the dictionary is None.

I believe addressing this discrepancy in JSON Schema generation between Python and other supported languages would greatly enhance the usability and compatibility of Quicktype. I hope this issue can be resolved to ensure a consistent experience for developers across all languages.

JSON Schema

{
    "$schema": "http://json-schema.org/draft-04/schema#",
        "properties": {
            "CommandArgs": {
                "required": [
                    "commandUri"
                ],
                "type": "object",
                "properties": {
                    "commandUri": {
                        "type": "string"
                    },
                    "parameters": {
                        "type": "object",
                        "additionalProperties": {
                            "type": "null"
                        }
                    }
                }
            },
            "CommandResp": {
                "type": "object",
                "properties": {
                    "resultFields": {
                        "type": "object",
                        "additionalProperties": {
                            "type": "null"
                        }
                    }
                }
            }
        }
}

Python Output

from typing import Dict, Optional

class CommandArgs:
    commandUri: str
    parameters: Optional[Dict[str, None]]  # <----- My Issue

    def __init__(self, commandUri: str, parameters: Optional[Dict[str, None]]) -> None:    # <----- My Issue
        self.commandUri = commandUri
        self.parameters = parameters

class CommandResp:
    resultFields: Optional[Dict[str, None]]  # <----- My Issue

    def __init__(self, resultFields: Optional[Dict[str, None]]) -> None:     # <----- My Issue
        self.resultFields = resultFields

class DRClass:
    CommandArgs: Optional[CommandArgs]
    CommandResp: Optional[CommandResp]

    def __init__(self, CommandArgs: Optional[CommandArgs], CommandResp: Optional[CommandResp]) -> None:
        self.CommandArgs = CommandArgs
        self.CommandResp = CommandResp

    @staticmethod
    def from_dict(obj: Any) -> 'DRClass':
        assert isinstance(obj, dict)
        CommandArgs = from_union([CommandArgs.from_dict, from_none], obj.get("CommandArgs"))  # <----- My Issue
        CommandResp = from_union([CommandResp.from_dict, from_none], obj.get("CommandResp"))  # <----- My Issue
        return DRClass(CommandArgs, CommandResp)

    def to_dict(self) -> dict:
        result: dict = {}
        if self.CommandArgs is not None:
            result["CommandArgs"] = from_union([lambda x: to_class(CommandArgs, x), from_none], self.CommandArgs)  # <----- My Issue
        if self.CommandResp is not None:
            result["CommandResp"] = from_union([lambda x: to_class(CommandResp, x), from_none], self.CommandResp)  # <----- My Issue
        return result