| 1 | // Copyright Louis Dionne 2013-2022 |
| 2 | // Distributed under the Boost Software License, Version 1.0. |
| 3 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) |
| 4 | |
| 5 | #include <boost/hana/assert.hpp> |
| 6 | #include <boost/hana/at.hpp> |
| 7 | #include <boost/hana/at_key.hpp> |
| 8 | #include <boost/hana/concept/sequence.hpp> |
| 9 | #include <boost/hana/concept/struct.hpp> |
| 10 | #include <boost/hana/define_struct.hpp> |
| 11 | #include <boost/hana/for_each.hpp> |
| 12 | #include <boost/hana/keys.hpp> |
| 13 | #include <boost/hana/length.hpp> |
| 14 | #include <boost/hana/not_equal.hpp> |
| 15 | #include <boost/hana/tuple.hpp> |
| 16 | |
| 17 | #include <iostream> |
| 18 | #include <limits> |
| 19 | #include <sstream> |
| 20 | #include <string> |
| 21 | #include <type_traits> |
| 22 | namespace hana = boost::hana; |
| 23 | |
| 24 | |
| 25 | struct Car { |
| 26 | BOOST_HANA_DEFINE_STRUCT(Car, |
| 27 | (std::string, brand), |
| 28 | (std::string, model) |
| 29 | ); |
| 30 | }; |
| 31 | |
| 32 | struct Person { |
| 33 | BOOST_HANA_DEFINE_STRUCT(Person, |
| 34 | (std::string, name), |
| 35 | (std::string, last_name), |
| 36 | (int, age) |
| 37 | ); |
| 38 | }; |
| 39 | |
| 40 | template <typename T> |
| 41 | std::enable_if_t<std::is_same<T, int>::value, |
| 42 | T> from_json(std::istream& in) { |
| 43 | T result; |
| 44 | in >> result; |
| 45 | return result; |
| 46 | } |
| 47 | |
| 48 | template <typename T> |
| 49 | std::enable_if_t<std::is_same<T, std::string>::value, |
| 50 | T> from_json(std::istream& in) { |
| 51 | char quote; |
| 52 | in >> quote; |
| 53 | |
| 54 | T result; |
| 55 | char c; |
| 56 | while (in.get(c&: c) && c != '"') { |
| 57 | result += c; |
| 58 | } |
| 59 | return result; |
| 60 | } |
| 61 | |
| 62 | |
| 63 | template <typename T> |
| 64 | std::enable_if_t<hana::Struct<T>::value, |
| 65 | T> from_json(std::istream& in) { |
| 66 | T result; |
| 67 | char brace; |
| 68 | in >> brace; |
| 69 | |
| 70 | // CAREFUL: This only works if the input JSON contains the fields in the |
| 71 | // same order as the struct declares them. |
| 72 | hana::for_each(hana::keys(result), [&](auto key) { |
| 73 | in.ignore(n: std::numeric_limits<std::streamsize>::max(), delim: ':'); |
| 74 | auto& member = hana::at_key(result, key); |
| 75 | using Member = std::remove_reference_t<decltype(member)>; |
| 76 | member = from_json<Member>(in); |
| 77 | }); |
| 78 | in >> brace; |
| 79 | return result; |
| 80 | } |
| 81 | |
| 82 | template <typename Xs> |
| 83 | std::enable_if_t<hana::Sequence<Xs>::value, |
| 84 | Xs> from_json(std::istream& in) { |
| 85 | Xs result; |
| 86 | char bracket; |
| 87 | in >> bracket; |
| 88 | hana::length(result).times.with_index([&](auto i) { |
| 89 | if (i != 0u) { |
| 90 | char comma; |
| 91 | in >> comma; |
| 92 | } |
| 93 | |
| 94 | auto& element = hana::at(result, i); |
| 95 | using Element = std::remove_reference_t<decltype(element)>; |
| 96 | element = from_json<Element>(in); |
| 97 | }); |
| 98 | in >> bracket; |
| 99 | return result; |
| 100 | } |
| 101 | |
| 102 | |
| 103 | int main() { |
| 104 | std::istringstream json(R"EOS( |
| 105 | [ |
| 106 | { |
| 107 | "name": "John", |
| 108 | "last_name": "Doe", |
| 109 | "age": 30 |
| 110 | }, |
| 111 | { |
| 112 | "brand": "BMW", |
| 113 | "model": "Z3" |
| 114 | }, |
| 115 | { |
| 116 | "brand": "Audi", |
| 117 | "model": "A4" |
| 118 | } |
| 119 | ] |
| 120 | )EOS" ); |
| 121 | |
| 122 | auto actual = from_json<hana::tuple<Person, Car, Car>>(in&: json); |
| 123 | |
| 124 | auto expected = hana::make_tuple(Person{.name: "John" , .last_name: "Doe" , .age: 30}, |
| 125 | Car{.brand: "BMW" , .model: "Z3" }, |
| 126 | Car{.brand: "Audi" , .model: "A4" }); |
| 127 | |
| 128 | BOOST_HANA_RUNTIME_CHECK(actual == expected); |
| 129 | } |
| 130 | |