| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- //
- // json_polymorphic_binder.hpp
- // json
- //
- // Created by Sam Jaffe on 11/15/18.
- // Copyright © 2018 Sam Jaffe. All rights reserved.
- //
- #pragma once
- #include "json_binder.hpp"
- #include <functional>
- #include <map>
- #include <string>
- namespace json { namespace binder {
- template <typename PtrBase, typename Derived>
- class polymorphic_pointer_binder : public binder_impl<PtrBase> {
- public:
- polymorphic_pointer_binder(binder<Derived> impl) : m_impl(impl) {}
- virtual binder_impl<PtrBase> * clone() const {
- return new polymorphic_pointer_binder(*this);
- }
- virtual void parse(PtrBase & v, char const *& data,
- parser::options opts) const {
- if (!strncmp(data, "null", 4))
- v = nullptr;
- else {
- v = PtrBase(new Derived);
- Derived & actual = dynamic_cast<Derived &>(*v);
- m_impl.parse(actual, data, opts);
- }
- }
- void write(PtrBase const & v, std::ostream & os) const {
- if (v)
- m_impl.write(dynamic_cast<Derived const &>(*v), os);
- else
- os << "null";
- }
- private:
- binder<Derived> m_impl;
- };
- template <typename Ptr> class polymorphic_binder : public binder_impl<Ptr> {
- private:
- // Maybe a reference type
- using BaseR = decltype(*std::declval<Ptr>());
- using Base = typename std::remove_reference<BaseR>::type;
- static_assert(std::is_polymorphic<Base>::value,
- "Must use a polymorphic type");
- public:
- polymorphic_binder() = default;
- virtual binder_impl<Ptr> * clone() const override {
- return new polymorphic_binder(*this);
- }
- template <typename Derived>
- polymorphic_binder & operator()(std::string const & k,
- binder_impl<Derived> const & v) {
- mapping.emplace(k, polymorphic_pointer_binder<Ptr, Derived>(v));
- reverse_lookup.emplace(
- k, [](Base const * p) { return dynamic_cast<Derived const *>(p); });
- return *this;
- }
- virtual void parse(Ptr & object, char const *& data,
- parser::options opts) const override {
- const char ch = json::helper::get_next_element(data);
- if (ch == '{') {
- parse_object(object, ++data, opts);
- } else {
- throw not_an_object_exception(typeid(Base).name());
- }
- }
- virtual void write(Ptr const & val, std::ostream & data) const override {
- data << '{';
- using pair_t = typename decltype(reverse_lookup)::value_type;
- Base const * p = std::addressof(*val);
- auto it =
- std::find_if(reverse_lookup.begin(), reverse_lookup.end(),
- [p](pair_t const & pair) { return pair.second(p); });
- if (it == reverse_lookup.end()) {
- throw std::domain_error("Unknown JSON binding object");
- }
- data << "\"@id\":\"" << it->first << "\",";
- data << "\"@value\":";
- mapping.find(it->first)->second.write(val, data);
- data << '}';
- }
- void parse_object(Ptr & object, char const *& data,
- parser::options opts) const {
- std::string key;
- fetch_expected_key("@id", data);
- if (json::helper::get_next_element(++data) != '"') {
- throw json::malformed_json_exception{
- std::string("Expected polymorphic id starting with '\"', got '") +
- *data + "' instead"};
- }
- json::helper::parse_string(key, data);
- auto it = mapping.find(key);
- if (it == mapping.end()) {
- throw json::malformed_json_exception{"Unknown polymorphic type-id: '" +
- key + "'"};
- }
- fetch_expected_key("@value", ++data);
- it->second.parse(object, ++data, opts);
- if (!*data)
- throw unterminated_json_object();
- else if (*data != '}')
- throw json::malformed_json_exception{
- std::string("Unexpected non-terminated object '") + *data + "'"};
- else
- ++data;
- }
- void fetch_expected_key(std::string const & expected,
- char const *& data) const {
- std::string key;
- if (json::helper::get_next_element(data) != '"') {
- throw malformed_object_key(*data);
- }
- json::helper::parse_string(key, data);
- if (key != expected) {
- throw json::malformed_json_exception{"Expecting '" + expected +
- "' polymorphic token, got '" +
- key + "' instead"};
- }
- if (json::helper::get_next_element(data) != ':') {
- throw malformed_object_association(*data);
- }
- }
- private:
- std::map<std::string, std::function<bool(Base const *)>> reverse_lookup;
- std::map<std::string, binder<Ptr>> mapping;
- };
- }}
|