如何在 JSON 中表示变体(总和类型)



>代数数据类型是一种准确描述数据的方法。

当涉及到JSON时,产品类型没有问题,即每个结构都逐个列出属于它们的道具。

然而,目前尚不清楚如何处理暗示一组道具或另一组道具的和类型,但不能同时暗示两者或它们的混合(除非它们有一些相似之处)。

所以

  • 如何在 JSON 中表示总和类型?
  • 如何区分同样可能的情况?

以以下变体类型为例。

data Tree = Empty
          | Leaf Int
          | Node Tree Tree

在 JSON 中,您可以使用以下三种形式来指定三种变体。

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

基本上,使用具有单个键值对的 JSON 对象,其中键是所选变体。

也许使用具有valuetag属性的对象表示法? 例如:

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

对象和特殊格式的字符串基本上是您在 JSON 中自描述数据类型的唯一真正选择。

让我们看看Cereal在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

输出:

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

当然,它会保存变体的替代索引,然后保存实际值。读取(反序列化)期间需要索引才能知道如何读取以下字段。如果没有该"索引"信息,则需要做大量工作来解析以下字段并通过反复试验推断类型信息,并且有时仍然无法解决歧义。

相关内容

  • 没有找到相关文章

最新更新