Is it possible to deserialize using Boost.Hana?
Asked Answered
P

2

6

I'm getting started with Boost.Hana and was wondering if there is a way to deserialize back into a Struct that is known to Boost.Hana. I know it's pretty simple to serialize such a Struct into a json string for example, but i did not find any information about the other way around. Is it currently just not possible to deserialize data with Boost.Hana or am i missing something?

Pointblank answered 11/10, 2015 at 13:42 Comment(0)
I
13

Hana is a metaprogramming library. It provides tools that can be used to build more complex functionality like serialization, but it does not provide such functionality itself. It's simply not the scope of that library. Also, regarding your particular use case; parsing is not an easy problem and other libraries like Boost.Spirit already try to solve it.

That being said, I sketched an example of using Hana to deserialize JSON. The result is neither efficient nor robust, but it should be enough to give you a glimpse at how Hana could be used to achieve something better. Solving this problem correctly would require implementing a parser combinator library à-la Boost.Spirit, which I won't do here. Here you go:

template <typename T>
  std::enable_if_t<std::is_same<T, int>::value,
T> from_json(std::istream& in) {
    T result;
    in >> result;
    return result;
}

template <typename T>
  std::enable_if_t<std::is_same<T, std::string>::value,
T> from_json(std::istream& in) {
    char quote;
    in >> quote;

    T result;
    char c;
    while (in.get(c) && c != '"') {
        result += c;
    }
    return result;
}


template <typename T>
  std::enable_if_t<hana::Struct<T>::value,
T> from_json(std::istream& in) {
    T result;
    char brace;
    in >> brace;

    hana::for_each(hana::keys(result), [&](auto key) {
        in.ignore(std::numeric_limits<std::streamsize>::max(), ':');
        auto& member = hana::at_key(result, key);
        using Member = std::remove_reference_t<decltype(member)>;
        member = from_json<Member>(in);
    });
    in >> brace;
    return result;
}

template <typename Xs>
  std::enable_if_t<hana::Sequence<Xs>::value,
Xs> from_json(std::istream& in) {
    Xs result;
    char bracket;
    in >> bracket;
    hana::length(result).times.with_index([&](auto i) {
        if (i != 0u) {
            char comma;
            in >> comma;
        }

        auto& element = hana::at(result, i);
        using Element = std::remove_reference_t<decltype(element)>;
        element = from_json<Element>(in);
    });
    in >> bracket;
    return result;
}

And then you can use it like

struct Car {
    BOOST_HANA_DEFINE_STRUCT(Car,
        (std::string, brand),
        (std::string, model)
    );
};

struct Person {
    BOOST_HANA_DEFINE_STRUCT(Person,
        (std::string, name),
        (std::string, last_name),
        (int, age)
    );
};

int main() {
    std::istringstream json(R"EOS(
        [
            {
                "name": "John",
                "last_name": "Doe",
                "age": 30
            },
            {
                "brand": "BMW",
                "model": "Z3"
            },
            {
                "brand": "Audi",
                "model": "A4"
            }
        ]
    )EOS");

    auto actual = from_json<hana::tuple<Person, Car, Car>>(json);

    auto expected = hana::make_tuple(Person{"John", "Doe", 30},
                                     Car{"BMW", "Z3"},
                                     Car{"Audi", "A4"});

    assert(actual == expected);
}

The full example is available here.

Immitigable answered 12/10, 2015 at 15:0 Comment(2)
Thanks, Louis. I was trying to write to members by using hana::for_each(hana::members(result), []...) to no avail. You have created an amazing library!Eustashe
Would it be possible to decode only available elements? For e.g. if 'Person' had an optional name and age? I've only been able to do this with 'for_each' and finding the accessor with name == 'last_name'. Unsure if there is a better way to do this with boost::hana.Fagen
K
1

The boost::hana json encoder is not compelete (it doesn't escape quotes for example): http://www.boost.org/doc/libs/1_61_0/libs/hana/doc/html/index.html#tutorial-introspection-json

To de-serialize, I'd use boost::spirit::x3: http://ciere.com/cppnow15/x3_docs/index.html

They have a json deserializer example: https://github.com/cierelabs/json_spirit

Karolekarolina answered 21/8, 2016 at 0:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.