JSON 的超集,用于数据建模






4.58/5 (4投票s)
JSON 的超集,具有改进的类型推断功能
JSON 的类型推断问题
有些 JSON 包含完整的类型信息,例如,我们来看以下代码片段
{
"users": [
{
"name": "Peter",
"email": "hello@peter.com"
},
{
"name": "Tom",
"email": "hello@tom.com"
}
]
}
我们可以清晰地推断出类型信息(作为一个 C# 类)
class User
{
public string Name { get; set; }
public string Email { get; set; }
}
一对多还是多对多?
但是,如果我们稍微复杂化一点,类型信息就会变得模糊
{
"users": [
{
"name": "Peter",
"email": "hello@peter.com",
"tasks": [
{
"title": "Shopping",
"description": "Buy: milk, bread, apples"
}
]
},
{
"name": "Tom",
"email": "hello@tom.com",
"tasks": []
}
]
}
现在我们仍然可以推断出 user
类
class User
{
public string Name { get; set; }
public string Email { get; set; }
public List<Task> Tasks { get; set; }
}
但是要推断出 task
类,我们需要知道 user
和 task
类之间的关系。 是一对多还是多对多? 一个任务是否只能有一个分配者,还是可以有多个分配者?
一种可能性是在一对多的情况下
class Task
{
public string Title { get; set; }
public string Description { get; set; }
public User Assignee { get; set; }
}
另一种是在多对多的情况下
class Task
{
public string Title { get; set; }
public string Description { get; set; }
public List<User> Assignees { get; set; }
}
同一类在多个地方
从 JSON 代码中推断类型时遇到的另一个问题是,当同一个类在同一代码片段中的多个地方出现时。 例如
{
"users": [
{
"userName": "Peter",
"email": "hello@peter.com",
"friends": [
{
"userName": "Tom",
"email": "hello@tom.com"
}
]
}
]
}
在这段代码中,不清楚 users
的 friends
也是 users
,还是它们是不同 friend
类的实例。
大多数人会同意以下解释
class User
{
public string Name { get; set; }
public string Email { get; set; }
public List<User> Friends { get; set; }
}
但以下解释同样有可能
class User
{
public string Name { get; set; }
public string Email { get; set; }
public List<Friend> Friends { get; set; }
}
class Friend
{
public string Name { get; set; }
public string Email { get; set; }
}
JSON 注解
注释
标准 JSON 不允许注释,但是 JSON 解析器库通常支持此功能。 一些支持注释的库: Json.NET, JSON5, JsonCpp。
由于它们被广泛支持,注释是实现 JSON 注解的一种很好的方式。 此外,如果您想编写包含注释 JSON 代码的文档(或类似本文的文章),通常可以使用语法高亮显示。
多对多注解
回到我们的第二个例子,我们可以通过添加 manyToMany
注解来明确用户和任务之间的关系。(假设一个任务可以有多个分配者。)
{
"users": [
{
"name": "Peter",
"email": "hello@peter.com",
"tasks": [
//manyToMany
{
"title": "Shopping",
"description": "Buy: milk, bread, apples"
}
]
},
{
"name": "Tom",
"email": "hello@tom.com",
"tasks": []
}
]
}
请注意,注释位于任务集合内部,而不是 task
对象内部。 另外请注意,只需要一个任务集合具有该注释。 行注释和块注释都受支持,因此 //manyToMany
和 /*manyToMany*/
是等效的。
类名注解
我们也可以使用一个声明 friends
类名的注解来明确 users
/ friends
示例
{
"users": [
{
"userName": "Peter",
"email": "hello@peter.com",
"friends": [
//class:user
//manyToMany
{
"userName": "Tom",
"email": "hello@tom.com"
}
]
}
]
}
请注意,集合名为 "users
"(复数),而类名为 "user
"(单数)。
这有什么用?
这种简单的语言可以在一个地方以非常紧凑的形式描述一组数据及其数据模型。 可以创建代码生成器,将这些带注释的 JSON 文件转换为
- Swagger (Open API Specification) 文件
- SQL 数据库初始化脚本
- 甚至完整的应用程序。 这方面的例子是 https://bootgen.com/,它可以从带注释的 JSON 创建 ASP.NET 5 - Vue.js 应用程序。
参考实现
您可以在 GitHub 上找到此语言的参考实现。 该库可以推断带注释的 JSON 文件的的数据模型,并使用 Scriban 模板为该数据模型生成代码。
历史
- 2021 年 4 月 27 日:第一个版本
- 2021 年 5 月 1 日:支持块注释