quicktype icon indicating copy to clipboard operation
quicktype copied to clipboard

[BUG]: Wrong c++ code generation

Open o3wiz opened this issue 1 year ago • 5 comments

Incorrect c++ code generation

Issue Type

quicktype output via cli is incorrect was output a hpp (c++) files.

Context

Windows 11,

Input Format: json schema Output Language: c++

CLI Version: 23.0.171

Description

I'm encountering unexpected output when running the following quicktype CLI command:

quicktype `
	--out "Student.hpp" `
	--lang c++ `
	--top-level Student `
	--src "student.schema.json" `
	--code-format with-struct `
	--type-style camel-case `
	--member-style camel-case `
	--enumerator-style pascal-case `
	--no-boost

Input Data

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "firstName": { "type": "string" },
    "lastName": { "type": "string" },
    "age": { "type": "integer" },
    "gender": { "enum": ["Male", "Female"] }
  },
  "required": ["firstName", "lastName", "age"]
}

Current Output

#pragma once

#include "json.hpp"

namespace quicktype {
    using nlohmann::json;

    #ifndef NLOHMANN_UNTYPED_quicktype_HELPER
    #define NLOHMANN_UNTYPED_quicktype_HELPER
    inline json get_untyped(const json & j, const char * property) {
        if (j.find(property) != j.end()) {
            return j.at(property).get<json>();
        }
        return json();
    }

    inline json get_untyped(const json & j, std::string property) {
        return get_untyped(j, property.data());
    }
    #endif

    struct age {
        std::string type;
    };

    struct gender {
        std::vector<std::string> genderEnum;
    };

    struct properties { // what is this??
        age firstName;
        age lastName;
        age age;
        gender gender;
    };

    struct student {
        std::string schema;
        std::string type;
        properties properties;
        std::vector<std::string> required;
    };
}
...

Issues Observed:

Inconsistent CLI vs Web App Output:

The generated C++ code differs between the CLI tool and the web app. Incorrect Schema Interpretation:

The age and gender fields are not being correctly represented as expected. The properties struct is structured strangely. Constraint Handling Issues:

Adding constraints (e.g., minimum and maximum for age) does not work as expected. Any insights into why this is happening and how to resolve it?

o3wiz avatar Feb 17 '25 12:02 o3wiz

While we're on the topic of stange C++ code being generated, Quicktype is generating a single struct from two separate objects, which are referenced by a oneOf directive.

Here's my schema:

// in another object
"credentials": {
    "type": "object",
    "oneOf": [
        {
            "$ref": "#/definitions/MqttUserCredentials"
        },
        {
            "$ref": "#/definitions/MqttCertCredentials"
        },
        {
            "type": "null"
        }
    ]
},

// more definitions
"MqttUserCredentials": {
    "type": "object",
    "properties": {
        "user_name": {
            "type": "string",
            "description": "The username for the client certificate"
        },
        "user_password": {
            "type": "string",
            "description": "The password for the client certificate"
        }
    },
    "required": [ "user_name", "user_password" ]
},
"MqttCertCredentials": {
    "type": "object",
    "properties": {
        "cert_path": {
            "type": "string",
            "description": "The path to the client certificate"
        },
        "key_path": {
            "type": "string",
            "description": "The path to the client private key"
        },
        "key_password": {
            "type": "string",
            "description": "The password for the client private key"
        }
    },
    "required": [ "cert_path", "key_path", "key_password" ]
},

And here is the resulting C++ code:

struct MqttCredentials {
    /**
    * The username for the client certificate
    */
    std::optional<std::string> user_name;
    /**
    * The password for the client certificate
    */
    std::optional<std::string> user_password;
    /**
    * The path to the client certificate
    */
    std::optional<std::string> cert_path;
    /**
    * The password for the client private key
    */
    std::optional<std::string> key_password;
    /**
    * The path to the client private key
    */
    std::optional<std::string> key_path;
};

struct MqttConfig {
    //<snip>
    std::optional<MqttCredentials> credentials;
    //<snip>
};

The expected behaviour is, especially considering the option --no-combine-classes is passed, that Quicktype generates an std::variant<MqttUserCredentials, MqttCertCredentials, std::nullptr_t> credentials field, instead of the nonsense it generates currently.

For reference, here's my CMake/Quicktype invocation:

####################################
##  CONFIG GENERATION             ##
####################################

# Define the input and output files
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/config/config.schema.json" "${CMAKE_CURRENT_BINARY_DIR}/cmake/config/config.schema.json" @ONLY)
set(INPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/config/config.schema.json")
set(OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/include/config")
set(OUTPUT_FILE "${OUTPUT_DIR}/MyConfig.hpp")

# Define the command to run
set(COMMAND "quicktype")
list(APPEND ARGUMENTS
    "--src-lang" "schema"
    "--lang" "c++"
    "--const-style" "west-const"
    "--namespace" "myapp::config"
    "--source-style" "multi-source"
    "--include-location" "global-include"
    "--type-style" "pascal-case"
    "--member-style" "underscore-case"
    "--enumerator-style" "upper-underscore-case"
    "--no-boost"
    "--code-format" "with-struct"
    "--no-combine-classes"
    "--out" "${OUTPUT_FILE}"
    "${INPUT_FILE}"
)

# Create the output directory
file(MAKE_DIRECTORY ${OUTPUT_DIR})

# Run the command
message(STATUS "Generating C++ code from JSON schema...")
message(STATUS "Command: ${COMMAND} ${ARGUMENTS}")
execute_process(COMMAND ${COMMAND} ${ARGUMENTS})

SimonCahill avatar Feb 20 '25 12:02 SimonCahill

This behaviour is also present on the web app.

SimonCahill avatar Feb 20 '25 12:02 SimonCahill

Hitting this issue right now with this kind of schema :

"material": {
  "oneOf": [
    {
      "type": "object",
      "properties": {
        "name": {
          "type": "string",
          "description": "this material's name"
        },
        "baseExtension": {
          "$ref": "material.schema.json#/definitions/baseExtension"
        },
        "metallicRoughnessExtension": {
          "$ref": "material.schema.json#/definitions/metallicRoughnessExtension"
        },
        "specularGlossinessExtension": {
          "$ref": "material.schema.json#/definitions/specularGlossinessExtension"
        }
      },
      "required": [
        "name"
      ]
    },
    {
      "type": "object",
      "properties": {
        "uri": {
          "$ref": "external.schema.json#/definitions/uri"
        }
      },
      "required": [
        "uri"
      ]
    }
  ]
}

The result in C++ makes no sense, quicktype making oneOf a vector of MaterialOneOf that are comprised of a SchemaType enum, a OneOfProperties struct and a vector of strings called "required" Quicktype seems to be completely lost, so much so that it seems to completely f*ck up other classes in the generated code with non-sensical class names with code such as this (center and halfSize are arrays of 3 floats, not "DiffuseTextureInfo" that has nothing to do with it) :

struct CubeDataProperties {
    DiffuseTextureInfo center;
    DiffuseTextureInfo half_size;
};

Removing oneOf from material makes Quicktype generate this code which doesn't make any sense for obvious reasons (NormalTextureInfoClass contains a single string called ref :

struct MaterialProperties {
    FogAreaClass name;
    NormalTextureInfoClass base_extension;
    NormalTextureInfoClass metallic_roughness_extension;
    NormalTextureInfoClass specular_glossiness_extension;
    NormalTextureInfoClass uri;
};

This seems like a MAJOR bug and I don't know why it's not getting more attention, Quicktype is completely unusable in its current state.

Gpinchon avatar Nov 29 '25 13:11 Gpinchon

It would seem the developers don't care much about Quicktype anymore. More and more issues are left unanswered, while more and more huge bugs arise.

I've stopped using QT entirely for C#, relying on my own code generator, which is far more reliable. Only for C++, because there is currently no suitable alternatives, and I don't have the time to develop one.

SimonCahill avatar Nov 29 '25 13:11 SimonCahill

Yeah I figured judging how last update was more than 6 months ago... Always a shame to see such useful projects die like that but that seems to happen quite frequently when private companies go open source

Gpinchon avatar Nov 29 '25 14:11 Gpinchon