beached / daw_json_link

Fast, convenient JSON serialization and parsing in C++
https://beached.github.io/daw_json_link/
Boost Software License 1.0
478 stars 31 forks source link

Error parsing key-value field after nullable field: "A value of known size was accessed past the end" (v.3.1) #334

Closed wxprospero closed 2 years ago

wxprospero commented 2 years ago

The issue:

#include <daw/json/daw_json_link.h>
#include <daw/json/daw_json_iterator.h>

#include <iostream>
#include <unordered_map>

struct PersonObject {
    int id{};
    std::string name;
    int age{};
    std::unordered_map<std::string, std::string> fields;
};

namespace daw::json {
    template<>
    struct json_data_contract<PersonObject> {
        static constexpr char const id[] = "id";
        static constexpr char const name[] = "name";
        static constexpr char const age[] = "age";
        static constexpr char const fields[] = "fields";
        using type = json_member_list<
            json_number<id, int>,
            json_string<name>,
            json_number_null<age, int >,
            json_key_value<fields, std::unordered_map<std::string, std::string>, std::string>
        >;
    };
}

int main() {
    std::string raw_json{R"_(
    {
        "id": 10,
        "name": "Jason",
        "fields": {"city": "Chicago", "Title": "Manager"},
        }
    )_"};
    PersonObject person = daw::json::from_json<PersonObject>(raw_json);

    return 0;
}

If you change the JSON source like this (adding in the nullable field):

    std::string raw_json{R"_(
    {
        "id": 10,
        "name": "Jason",
        "age": 25,
        "fields": {"city": "Chicago", "Title": "Manager"},
        }
    )_"};

the error goes away.

This only happens with key-value types after a nullable number. If you change the json_key_value type to be a non-nullable json_number, no error is raised, even if the preceding nullable number is omitted:

#include <daw/json/daw_json_link.h>
#include <daw/json/daw_json_iterator.h>

#include <iostream>

struct PersonObject {
    int id{};
    std::string name;
    int age{};
    int id2{};
};

namespace daw::json {
    template<>
    struct json_data_contract<PersonObject> {
        static constexpr char const id[] = "id";
        static constexpr char const name[] = "name";
        static constexpr char const age[] = "age";
        static constexpr char const id2[] = "id2";
        using type = json_member_list<
            json_number<id, int>,
            json_string<name>,
            json_number_null<age, int >,
            json_number<id2, int>
        >;
    };
}

int main() {
    std::string raw_json{R"_(
    {
        "id": 10,
        "name": "Jason",
        "id2": 11
        }
    )_"};
    PersonObject person = daw::json::from_json<PersonObject>(raw_json);

    return 0;
}
beached commented 2 years ago

Thanks for reporting that, I have found the error, looking into a fix

beached commented 2 years ago

Issue was debug code that had an incorrect condition. Fixed via https://github.com/beached/daw_json_link/pull/335

wxprospero commented 2 years ago

While you're here, is "json_key_value_null" broken as well? Why does changing "json_key_value" to "json_key_value_null" break this, whereas with other types (like json_number or json_string), changing it to nullable requires no extra or change to template parameters? An unordered_map is default constructable, and value initializable, isn't it?

#include <daw/json/daw_json_link.h>
#include <daw/json/daw_json_iterator.h>

#include <iostream>
#include <unordered_map>

struct PersonObject {
    int id{};
    std::string name;
    std::unordered_map<std::string, std::string> fields;
};

namespace daw::json {
    template<>
    struct json_data_contract<PersonObject> {
        static constexpr char const id[] = "id";
        static constexpr char const name[] = "name";
        static constexpr char const fields[] = "fields";
        using type = json_member_list<
            json_number<id, int>,
            json_string<name>,
            json_key_value_null<fields, std::unordered_map<std::string, std::string>, std::string>
        >;
    };
}

int main() {
    std::string raw_json{R"_(
    {
        "id": 10,
        "name": "Jason",
        "fields": {"city": "Chicago", "Title": "Manager"},
        }
    )_"};
    PersonObject person = daw::json::from_json<PersonObject>(raw_json);

    return 0;
}

There is also no documentation for how to use json_key_value_null, that I can find

beached commented 2 years ago

Moving this to another issue.