quicktype
quicktype copied to clipboard
Issue with optional "any" type in C++
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 (https://github.com/nlohmann/json/issues/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.