json_polymorphic_binder.hpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. //
  2. // json_polymorphic_binder.hpp
  3. // json
  4. //
  5. // Created by Sam Jaffe on 11/15/18.
  6. // Copyright © 2018 Sam Jaffe. All rights reserved.
  7. //
  8. #pragma once
  9. #include "json_binder.hpp"
  10. #include <functional>
  11. #include <map>
  12. #include <string>
  13. namespace json { namespace binder {
  14. template <typename PtrBase, typename Derived>
  15. class polymorphic_pointer_binder : public binder_impl<PtrBase> {
  16. public:
  17. polymorphic_pointer_binder(binder<Derived> impl) : m_impl(impl) {}
  18. virtual binder_impl<PtrBase> * clone() const {
  19. return new polymorphic_pointer_binder(*this);
  20. }
  21. virtual void parse(PtrBase & v, char const *& data,
  22. parser::options opts) const {
  23. v = PtrBase(new Derived);
  24. Derived & actual = dynamic_cast<Derived &>(*v);
  25. m_impl.parse(actual, data, opts);
  26. }
  27. void write(PtrBase const & v, std::ostream & os) const {
  28. m_impl.write(dynamic_cast<Derived const &>(*v), os);
  29. }
  30. private:
  31. binder<Derived> m_impl;
  32. };
  33. template <typename Ptr> class polymorphic_binder : public binder_impl<Ptr> {
  34. private:
  35. // Maybe a reference type
  36. using BaseR = decltype(*std::declval<Ptr>());
  37. using Base = typename std::remove_reference<BaseR>::type;
  38. static_assert(std::is_polymorphic<Base>::value,
  39. "Must use a polymorphic type");
  40. public:
  41. polymorphic_binder() = default;
  42. virtual binder_impl<Ptr> * clone() const override {
  43. return new polymorphic_binder(*this);
  44. }
  45. template <typename Derived>
  46. polymorphic_binder & operator()(std::string const & k,
  47. binder_impl<Derived> const & v) {
  48. mapping.emplace(k, polymorphic_pointer_binder<Ptr, Derived>(v));
  49. reverse_lookup.emplace(
  50. k, [](Base const * p) { return dynamic_cast<Derived const *>(p); });
  51. return *this;
  52. }
  53. virtual void parse(Ptr & object, char const *& data,
  54. parser::options opts) const override {
  55. const char ch = json::helper::get_next_element(data);
  56. if (ch == '{') {
  57. parse_object(object, ++data, opts);
  58. } else if (!strncmp(data, "null", 4)) {
  59. object = nullptr;
  60. } else {
  61. throw not_an_object_exception(typeid(Base).name());
  62. }
  63. }
  64. virtual void write(Ptr const & val, std::ostream & data) const override {
  65. if (!val) {
  66. data << "null";
  67. } else {
  68. write_object(val, data);
  69. }
  70. }
  71. private:
  72. void parse_object(Ptr & object, char const *& data,
  73. parser::options opts) const {
  74. std::string key;
  75. fetch_expected_key("@id", data);
  76. if (json::helper::get_next_element(++data) != '"') {
  77. throw json::malformed_json_exception{
  78. std::string("Expected polymorphic id starting with '\"', got '") +
  79. *data + "' instead"};
  80. }
  81. json::helper::parse_string(key, data);
  82. auto it = mapping.find(key);
  83. if (it == mapping.end()) {
  84. throw json::malformed_json_exception{"Unknown polymorphic type-id: '" +
  85. key + "'"};
  86. }
  87. fetch_expected_key("@value", ++data);
  88. it->second.parse(object, ++data, opts);
  89. if (!*data)
  90. throw unterminated_json_object();
  91. else if (*data != '}')
  92. throw json::malformed_json_exception{
  93. std::string("Unexpected non-terminated object '") + *data + "'"};
  94. else
  95. ++data;
  96. }
  97. void write_object(Ptr const & val, std::ostream & data) const {
  98. data << '{';
  99. using pair_t = typename decltype(reverse_lookup)::value_type;
  100. Base const * p = std::addressof(*val);
  101. auto it =
  102. std::find_if(reverse_lookup.begin(), reverse_lookup.end(),
  103. [p](pair_t const & pair) { return pair.second(p); });
  104. if (it == reverse_lookup.end()) {
  105. throw std::domain_error("Unknown JSON binding object");
  106. }
  107. data << "\"@id\":\"" << it->first << "\",";
  108. data << "\"@value\":";
  109. mapping.find(it->first)->second.write(val, data);
  110. data << '}';
  111. }
  112. void fetch_expected_key(std::string const & expected,
  113. char const *& data) const {
  114. std::string key;
  115. if (json::helper::get_next_element(data) != '"') {
  116. throw malformed_object_key(*data);
  117. }
  118. json::helper::parse_string(key, data);
  119. if (key != expected) {
  120. throw json::malformed_json_exception{"Expecting '" + expected +
  121. "' polymorphic token, got '" +
  122. key + "' instead"};
  123. }
  124. if (json::helper::get_next_element(data) != ':') {
  125. throw malformed_object_association(*data);
  126. }
  127. }
  128. std::map<std::string, std::function<bool(Base const *)>> reverse_lookup;
  129. std::map<std::string, binder<Ptr>> mapping;
  130. };
  131. }}