glideapps / quicktype

Generate types and converters from JSON, Schema, and GraphQL

Home Page:https://app.quicktype.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Issue with optional "any" type in C++

flounderpinto opened this issue · comments

Given the following JSON schema:

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "definitions": {
    "example": {
      "type": "object",
        "required": [
          "foo",
          "bar"
        ],
        "additionalProperties": false,
        "properties": {
          "foo": {
            "type": "boolean"
          },
          "bar": {
            "type": "string"
          },
          "alpha": {
          },
          "beta": {
            "type": "integer"
          }
        }
    }
  },
  "$ref": "#/definitions/example"
}

The following C++ is generated (with the --hide-null-optional and --no-boost options):

//  To parse this JSON data, first install
//
//      json.hpp  https://github.com/nlohmann/json
//
//  Then include this file, and then do
//
//     Test data = nlohmann::json::parse(jsonString);

#pragma once

#include "json.hpp"

#include <optional>
#include <stdexcept>
#include <regex>

#ifndef NLOHMANN_OPT_HELPER
#define NLOHMANN_OPT_HELPER
namespace nlohmann {
    template <typename T>
    struct adl_serializer<std::shared_ptr<T>> {
        static void to_json(json & j, const std::shared_ptr<T> & opt) {
            if (!opt) j = nullptr; else j = *opt;
        }

        static std::shared_ptr<T> from_json(const json & j) {
            if (j.is_null()) return std::unique_ptr<T>(); else return std::unique_ptr<T>(new T(j.get<T>()));
        }
    };
}
#endif

namespace quicktype {
    using nlohmann::json;

    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());
    }

    template <typename T>
    inline std::shared_ptr<T> get_optional(const json & j, const char * property) {
        if (j.find(property) != j.end()) {
            return j.at(property).get<std::shared_ptr<T>>();
        }
        return std::shared_ptr<T>();
    }

    template <typename T>
    inline std::shared_ptr<T> get_optional(const json & j, std::string property) {
        return get_optional<T>(j, property.data());
    }

    class Test {
        public:
        Test() = default;
        virtual ~Test() = default;

        private:
        nlohmann::json alpha;
        std::string bar;
        std::shared_ptr<int64_t> beta;
        bool foo;

        public:
        const nlohmann::json & get_alpha() const { return alpha; }
        nlohmann::json & get_mutable_alpha() { return alpha; }
        void set_alpha(const nlohmann::json & value) { this->alpha = value; }

        const std::string & get_bar() const { return bar; }
        std::string & get_mutable_bar() { return bar; }
        void set_bar(const std::string & value) { this->bar = value; }

        std::shared_ptr<int64_t> get_beta() const { return beta; }
        void set_beta(std::shared_ptr<int64_t> value) { this->beta = value; }

        const bool & get_foo() const { return foo; }
        bool & get_mutable_foo() { return foo; }
        void set_foo(const bool & value) { this->foo = value; }
    };
}

namespace nlohmann {
    void from_json(const json & j, quicktype::Test & x);
    void to_json(json & j, const quicktype::Test & x);

    inline void from_json(const json & j, quicktype::Test& x) {
        x.set_alpha(quicktype::get_untyped(j, "alpha"));
        x.set_bar(j.at("bar").get<std::string>());
        x.set_beta(quicktype::get_optional<int64_t>(j, "beta"));
        x.set_foo(j.at("foo").get<bool>());
    }

    inline void to_json(json & j, const quicktype::Test & x) {
        j = json::object();
        if (x.get_alpha()) {
            j["alpha"] = x.get_alpha();
        }
        j["bar"] = x.get_bar();
        if (x.get_beta()) {
            j["beta"] = x.get_beta();
        }
        j["foo"] = x.get_foo();
    }
}

Notice that for all types, if they're optional, they're std::shared_ptr<>. Then in the to_json() methods, there's an (if x.pointer()) ... method, to check for null before adding to the json object. The "any" type is a nlohmann::json object, and keeping with the same paradigm, an optional "any" type should be a std::shared_ptr<nlohmann::json>, but is generated as a nlohmann::json object instead. This almost works since an empty json object does have the concept of "null". Unfortunately, nlohmann/json does not have a bool operator (nlohmann/json#951), so the if (x.get_alpha()) line above is invalid.

I think this all boils down to one bad line: https://github.com/quicktype/quicktype/blob/master/src/quicktype-core/language/CPlusPlus.ts#L851
Simply removing that line seems to fix the problem.