How do I represent variants (sum-types) in JSON?
Asked Answered
A

3

12

Algebraic data types are a way to accurately describe the data.

When it comes to JSON there is no problem with product types, that is each structure is listing props one-by-one that belong to them.

However it's not clear what to do with a sum-types that imply one set of props or the other, but not both or their mix (unless they share some similarities).

So

  • how can you represent a sum type in JSON?
  • how can you differentiate between the equally possible cases?
Adinaadine answered 3/3, 2014 at 18:5 Comment(0)
A
10

Take for example the following variant type.

data Tree = Empty
          | Leaf Int
          | Node Tree Tree

In JSON you can use the following three forms to specify the three variants.

Variant | JSON
--------+---------------
Empty   | null
--------+---------------
Leaf    | {
        |   "leaf": 7
        | }
--------+---------------
Node    | {
        |   "node": [
        |     <tree>,
        |     <tree>
        |   ]
        | }

Basically, use a JSON object with a single key-value pair, where the key is the selected variant.

Anagram answered 3/3, 2014 at 18:9 Comment(1)
Nice ASCII graphics :)Tropology
L
6

Perhaps using object notation with value and tag properties? E.g.:

{
    "someVariant": {
        "value": 25,
        "tag":   "currentFormOfTheVariant"
    }
}

Object and specially-formatted strings are basically your only real options for self-describing data types in JSON.

Linotype answered 3/3, 2014 at 18:9 Comment(0)
F
0

Let's see what Cereal would do in C++.

    std::ostringstream oss;
    {
        cereal::JSONOutputArchive oa{oss};

        std::variant<double, std::string> v1{std::string{"hello"}};
        std::variant<double, std::string> v2{3.14};

        oa << cereal::make_nvp("v1", v1);
        oa << cereal::make_nvp("v2", v2);
    }
    std::cout << oss.str() << std::endl;

https://godbolt.org/z/fx1zcYM84

output:

{
    "v1": {
        "index": 1,
        "data": "hello"
    },
    "v2": {
        "index": 0,
        "data": 3.14
    }
}

Of course, it would save the alternative index of the variant and then the actual value. The index is needed during reading (deserialization) to know how to read the following field. Without that "index" information, it would need to do much work to parse the following field and deduce type information by trial and error and still sometimes fail to resolve ambiguities.

Fugate answered 2/6, 2023 at 22:55 Comment(2)
say you got an input of this data, how would you process it all cases covered guaranteed?Tropology
@TridentD'Gao, i don’t understand the question in your comment . do you want to make the reading more failsafe? you want to read all the alternatives first and decide what to use from the index?Fugate

© 2022 - 2024 — McMap. All rights reserved.