nlohmann / json

JSON for Modern C++

Home Page:https://json.nlohmann.me

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unexpected conversion from 'true'/'false' to some integer types

jboelterintc opened this issue · comments

Description

Converting from "value": true or "value": false behaves unexpectedly and differently for certain integer types.

I was expecting all conversions from a boolean true/false to integer value to fail, however it is stored as a 1 or 0.

Reproduction steps

Given:

template<typename T>
class Value {
public:
    T value;
private:
    NLOHMANN_DEFINE_TYPE_INTRUSIVE(Value<T>, value);
};

This will result in a 1 in value.

auto v = R"({
    "value" : true
})"_json.get<Value<int>>();

Using uint64_t will throw a type_error.

auto v = R"({
    "value" : true
})"_json.get<Value<uint64_t>>();

Expected vs. actual results

I expected all boolean to integer conversions to fail. Instead a true -> 1 and false -> 0 for certain integer values.

Various integer conversions - https://godbolt.org/z/7Wrh6EanW

The uint64_t case is taking the path through get_arithmetic_value

template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
{
    get_arithmetic_value(j, val);
}

The int case is taking the path through

struct from_json_fn
{
    template<typename BasicJsonType, typename T>
    auto operator()(const BasicJsonType& j, T&& val) const
    noexcept(noexcept(from_json(j, std::forward<T>(val))))
    -> decltype(from_json(j, std::forward<T>(val)))
    {
        return from_json(j, std::forward<T>(val));
    }
};

// which calls

template < typename BasicJsonType, typename ArithmeticType,
           enable_if_t <
               std::is_arithmetic<ArithmeticType>::value&&
               !std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&&
               !std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&&
               !std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&
               !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
               int > = 0 >
inline void from_json(const BasicJsonType& j, ArithmeticType& val)
{
//...

        case value_t::boolean:
        {
            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
            break;
        }
//...
}

Minimal code example

https://godbolt.org/z/fs4frqz7G

#include <cassert>
#include <iostream>

#include <nlohmann/json.hpp>

template<typename T>
class Value {
public:
    T value;
private:
    NLOHMANN_DEFINE_TYPE_INTRUSIVE(Value<T>, value);
};

int main() {
    try {
        std::cout << "uint64_t" << std::endl;
        auto v = R"({
            "value" : true
        })"_json.get<Value<uint64_t>>();

        assert(false);
    } catch (const nlohmann::detail::type_error& e) {
        std::cout << e.what() << std::endl;
        std::cout << "ok - exception expected" << std::endl;
    }

    try {
        std::cout << "int" << std::endl;
        auto v = R"({
            "value" : true
        })"_json.get<Value<int>>();

        assert(false);
    } catch (const nlohmann::detail::type_error& e) {
        std::cout << e.what() << std::endl;
        std::cout << "ok - exception expected" << std::endl;
    }
}

Error messages

No response

Compiler and operating system

Latest MSVC & Clang

Library version

3.11.3

Validation

I am having the same issue and apologies if I am not understanding this correctly as I am new to C++ and have only just started using this library, but it looks like the integer value is not being considered a basic_json and so is calling the explicit cast from_json. At least the comment above the function leads me to believe this.
// overload for arithmetic types, not chosen for basic_json template arguments
// (BooleanType, etc..); note: Is it really necessary to provide explicit
// overloads for boolean_t etc. in case of a custom BooleanType which is not
// an arithmetic type?