Skip to content

Latest commit

 

History

History
386 lines (313 loc) · 10.6 KB

File metadata and controls

386 lines (313 loc) · 10.6 KB

jsoncons::reflect::json_conv_traits

#include <jsoncons/reflect/json_conv_traits.hpp>

json_conv_traits defines a compile time template based interface for conversion between a basic_json value and a value of some other type. json_conv_traits implementations must specialize a traits class for a type T:

template <typename Json,typename T,typename Enable=void>
struct json_conv_traits
{
    static constexpr bool is(const Json& j) noexcept;

    template<typename Alloc,typename TempAlloc>
    static conversion_result<T> try_as(const allocator_set<Alloc,TempAlloc>&, const Json& j);

    template <typename Alloc, typename TempAlloc>
    static Json to_json(const allocator_set<Alloc,TempAlloc>& aset, const T& val);
};

The function

json_conv_traits<Json,T>::is(const Json& j) noexcept

indictates whether j satisfies the requirements of type T. This function supports the type selection strategy when converting a Json value to the proper derived class in the polymorphic case, and when converting a Json value to the proper alternative type in the variant case.

The function

template <typename Alloc, typename TempAlloc>
conversion_result<T> try_as(const allocator_set<Alloc,TempAlloc>& aset, const Json& j) 

tries to convert j to a value of type T and returns an std::expected-like result (like std::expected<T,jsoncons::conversion_error>.)

The function

template <typename Alloc, typename TempAlloc>
Json to_json(const allocator_set<Alloc,TempAlloc>& aset, const T& val)

tries to convert val into a Json value.

jsoncons includes specializiations for most types in the standard library. And it includes convenience macros that make specializing json_conv_traits for your own types easier.

See also

allocator_set
conversion_result

Examples

Optional values

#include <jsoncons/json.hpp>
#include <string>
#include <optional>
#include <iostream>
#include <cassert>

namespace ns {

struct OptionalExample {

    std::string text;
    std::optional<std::string> optional;
    std::optional<std::string> optional_skip;

    explicit OptionalExample() = default;
};

} // namespace ns

namespace jsoncons {
namespace reflect {

template <typename Json>
struct json_conv_traits<Json, ns::OptionalExample>
{
    using value_type = ns::OptionalExample;

    static bool is(const Json& j) noexcept
    {
        return j.is_object() && j.contains("text") && j.contains("optional");
    }
    template <typename Alloc, typename TempAlloc>
    static conversion_result<value_type> try_as(const allocator_set<Alloc,TempAlloc>&, const Json& j)
    {
        using result_type = conversion_result<value_type>;

        if (!j.is_object())
        {
            return result_type(jsoncons::unexpect, conv_errc::not_map, "ns::OptionalExample");
        }

        value_type val;
        {
            auto it = j.find("text");
            if (it == j.object_range().end())
            {
                return result_type(jsoncons::unexpect, conv_errc::missing_required_member, "text");
            }
            auto r = it->value().template try_as<std::string>();
            if (!r)
            {
                return result_type(unexpect, r.error().code());
            }
            val.text = std::move(*r);
        }
        {
            auto it = j.find("optional");
            if (it != j.object_range().end())
            {
                auto r = it->value().template try_as<std::string>();
                if (!r)
                {
                    return result_type(unexpect, r.error().code());
                }
                val.optional = std::move(*r);
            }
        }
        {
            auto it = j.find("optional_skip");
            if (it != j.object_range().end())
            {
                auto r = it->value().template try_as<std::string>();
                if (!r)
                {
                    return result_type(unexpect, r.error().code());
                }
                val.optional_skip = std::move(*r);
            }
        }

        return result_type(std::move(val));
    }
    template <typename Alloc, typename TempAlloc>
    static Json to_json(const allocator_set<Alloc,TempAlloc>& aset, const ns::OptionalExample& val)
    {
        Json j = jsoncons::make_obj_using_allocator<Json>(aset.get_allocator(), json_object_arg);
        j.try_emplace("text", val.text);
        if (val.optional)
        {
            j.try_emplace("optional", *val.optional);
        }
        if (val.optional_skip)
        {
            j.try_emplace("optional_skip", *val.optional_skip);
        }
        return j;
    }
};
} // namespace reflect
} // namespace jsoncons

int main()
{
    std::error_code ec;

    ns::OptionalExample val;
    val.text = "Sample!";
    val.optional = "Available Value";

    std::string buffer;
    auto wresult = jsoncons::try_encode_json_pretty(val, buffer);
    if (!wresult)
    {
        std::cout << wresult.error().message() << "\n";
        exit(1);
    }
    std::cout << buffer << "\n";
}

Output:

{
    "text": "Sample!",
    "optional": "Available Value"
}

Remarks

  • If you're using C++20 or higher, you can use std::make_obj_using_allocator in to_json instead of jsoncons::make_obj_using_allocator.

  • To save typing and enhance readability, you can use the convenience macro JSONCONS_N_MEMBER_TRAITS to generate the traits classes,

JSONCONS_N_MEMBER_TRAITS(OptionalExample, 1, text, optional, optional_skip);

Uses allocator construction example

#include <jsoncons/json.hpp>
#include <../examples/src/common/mock_stateful_allocator.hpp>
#include <scoped_allocator>
#include <vector>
#include <string>
#include <iostream>
#include <cassert>

namespace ns {

template <typename Alloc>
struct book
{
    using allocator_type = Alloc;

    using char_allocator_type = typename std::allocator_traits<Alloc>:: template rebind_alloc<char>;
    using string_type = std::basic_string<char, std::char_traits<char>, char_allocator_type>;

    string_type author;
    string_type title;
    double price{0};

    book(const Alloc& alloc)
        : author(alloc), title(alloc)
    {
    }

    book(book&& other, const Alloc& alloc)
        : author(std::move(other.author), alloc), title(std::move(other.title), alloc)
    {
    }
};

} // namespace ns

namespace jsoncons {
namespace reflect {

template <typename Json, typename Alloc>
struct json_conv_traits<Json, ns::book<Alloc>>
{
    using value_type = ns::book<Alloc>;

    static bool is(const Json& j) noexcept
    {
        return j.is_object() && j.contains("author") &&
            j.contains("title") && j.contains("price");
    }
    template <typename Alloc, typename TempAlloc>
    static conversion_result<value_type> try_as(const allocator_set<Alloc, TempAlloc>& aset, const Json& j)
    {
        using result_type = conversion_result<value_type>;

        if (!j.is_object())
        {
            return result_type(jsoncons::unexpect, conv_errc::not_map, "ns::book");
        }

        auto val = jsoncons::make_obj_using_allocator<value_type>(aset.get_allocator());

        {
            auto it = j.find("author");
            if (it == j.object_range().end())
            {
                return result_type(jsoncons::unexpect, conv_errc::missing_required_member, "author");
            }
            auto r = it->value().template try_as<typename value_type::string_type>(aset);
            if (!r)
            {
                return result_type(unexpect, r.error().code());
            }
            val.author = std::move(*r);
        }
        {
            auto it = j.find("title");
            if (it == j.object_range().end())
            {
                return result_type(jsoncons::unexpect, conv_errc::missing_required_member, "title");
            }
            auto r = it->value().template try_as<typename value_type::string_type>(aset);
            if (!r)
            {
                return result_type(jsoncons::unexpect, r.error().code());
            }
            val.title = std::move(*r);
        }
        {
            auto it = j.find("price");
            if (it == j.object_range().end())
            {
                return result_type(jsoncons::unexpect, conv_errc::missing_required_member, "price");
            }
            auto r = it->value().template try_as<double>(aset);
            if (!r)
            {
                return result_type(jsoncons::unexpect, r.error().code(), "price");
            }
            val.price = *r;
        }

        return result_type(std::move(val));
    }

    template <typename Alloc, typename TempAlloc>
    static Json to_json(const allocator_set<Alloc, TempAlloc>& aset, const value_type& val)
    {
        auto j = jsoncons::make_obj_using_allocator<Json>(aset.get_allocator(), json_object_arg);
        j.try_emplace("author", val.author);
        j.try_emplace("title", val.title);
        j.try_emplace("price", val.price);
        return j;
    }
};

} // namespace reflect
} // namespace jsoncons

template <typename T>
using cust_allocator = std::scoped_allocator_adaptor<mock_stateful_allocator<T>>;

using book_type = ns::book<cust_allocator<char>>;
using books_type = std::vector<book_type, cust_allocator<book_type>>;

int main()
{
    const std::string input = R"(
    [
        {
            "author" : "Haruki Murakami",
            "title" : "Kafka on the Shore",
            "price" : 25.17
        },
        {
            "author" : "Charles Bukowski",
            "title" : "Pulp",
            "price" : 22.48
        }
    ]
    )";

    cust_allocator<book_type> alloc(1);
    auto aset = jsoncons::make_alloc_set(alloc);
    auto rin = jsoncons::try_decode_json<books_type>(aset, input);
    assert(rin);

    std::string output;
    auto rout = jsoncons::try_encode_json_pretty(aset, *rin, output);
    assert(rout);
    std::cout << output << "\n";
}

Output:

[
    {
        "author": "Haruki Murakami",
        "title": "Kafka on the Shore",
        "price": 0.0
    },
    {
        "author": "Charles Bukowski",
        "title": "Pulp",
        "price": 0.0
    }
]

Remarks

  • If you're using C++20 or higher, you can use std::make_obj_using_allocator in try_as and to_json instead of jsoncons::make_obj_using_allocator.

  • To save typing and enhance readability, you can use the convenience macro JSONCONS_TMPL_ALL_MEMBER_TRAITS to generate the traits classes,

    JSONCONS_TMPL_ALL_MEMBER_TRAITS(1, ns::book, author, title, price)