Wjybxx.Dson
2.1.2
The package has been merged into commons
See the version list below for details.
dotnet add package Wjybxx.Dson --version 2.1.2
NuGet\Install-Package Wjybxx.Dson -Version 2.1.2
<PackageReference Include="Wjybxx.Dson" Version="2.1.2" />
paket add Wjybxx.Dson --version 2.1.2
#r "nuget: Wjybxx.Dson, 2.1.2"
// Install Wjybxx.Dson as a Cake Addin #addin nuget:?package=Wjybxx.Dson&version=2.1.2 // Install Wjybxx.Dson as a Cake Tool #tool nuget:?package=Wjybxx.Dson&version=2.1.2
Dson(DataScript Object Notation)
Dson是一个有点奇怪的配置文件格式,但这也许会成为流行的配置文件格式。
Dson同时设计了二进制和文本格式,二进制用于网络传输,文本格式用于配置文件;另外,还提供了数字表示字段的高压缩率二进制版本。
Dson核心包不提供Dson到对象的编解码实现,只包含Dson的二进制流和文本解析实现;Dson支持复杂的数据结构,同时还设计了对象头,因此实现自己的Codec是很容易的。
Dson最初是为序列化而创建的,但我们在这里只讨论文本格式,二进制格式详见
Dson二进制流,
另外,Dson的一些设计可能与你期望的不同,为避免频繁提问,我将一些设计理由进行了整理,见
Dson设计过程中的一些反思。
另外,我将一些特殊的测试用例或者说模板整理了一下,见
Dson测试用例。
Dson的目标
- 简单
- 精确
- 易于编写和阅读
- 易于维护(修改)
- 易于解析和输出
- 易于扩展
- 高性能
Dson的特性
- 支持注释
- 支持指定值类型,精确解析
- 支持行合并,长内容行随意切割
- 支持纯文本输入,所见即所得,真正摆脱转义字符 - 也支持混合输入模式
- 引号不是必须的
- 支持对象头,可自定义对象头内容
- 支持读取Json
文本示例(标准)
以下文本为手写文本,用到的特殊标签包括:文本块"""
、 单行纯文本sL
。
(文本见测试用例DsonTextReaderTest2.java
)
// 以下是一个简单的DsonObject示例
{@{clsName:MyClassInfo, guid :10001, flags: 0}
name : wjybxx,
age: 28,
pos :{@{Vector3} x: 0, y: 0, z: 0},
address: [
beijing,
chengdu
],
intro:
"""
我是wjybxx,是一个游戏开发者,Dson是我设计的文档型数据表达法,你可以通过github联系到我。
thanks
"""
, url: @sL https://www.github.com/hl845740757
, time: {@dt date: 2023-06-17, time: 18:37:00, millis: 100, offset: +08:00}
}
Dson类型系统
声明类型
我们可以通过@label
声明一个元素的类型,一般是修饰下一个元素,元素可以是 key,也可以是value,取决于上下文。
普通value类型,可在value的前方通过@
声明类型;object和array则在{}和[]内声明其类型,且声明时和外层括号之间无空格。
ps: 当@作用于普通值类型和内置简单结构体时,我们称@
声明的是其类型;当@
作用于object和array时,我们称@
声明的是对象的头信息。
内建类型
Dson支持的值类型和内置结构体包括:
标签 | 类型 | 枚举 | 含义 | 内置结构体 | 格式或示例 |
---|---|---|---|---|---|
i | int32 | 1 | 32位整型 | @i 123 <br> @i 0xFF | |
L | int64 | 2 | 64位长整型,大写L | @L 123 <br> @L 0xFF | |
f | float | 3 | 32位浮点数 | @f 1.0 | |
d | double | 4 | 64位浮点数 | @d 1.5 <br> 1.5 | |
b | bool | 5 | bool值 | @b true <br> true <br/> @b 1 | |
s | string | 6 | 字符串 | "10" <br> abc | |
N | null | 7 | null,大写N | @N null <br> null | |
bin | binary | 11 | 二进制,带类型标签 | {<br> int32 type;<br> byte[] data; <br>} | 格式固定二元数组 [@bin type, data] <br> [@bin 1, FFFE] <br/> [@bin 1, null] |
ei | extInt32 | 12 | 带类型标签的int32 | {<br> int32 type;<br> int32 value;<br> bool hasVal; <br>} | 格式固定二元数组 [@ei type, value] <br> [@ei 1, 10086] <br> [@ei 1, null] |
eL | extInt64 | 13 | 带类型标签的int64 | {<br> int32 type;<br> int64 value;<br> bool hasVal; <br>} | 格式固定二元数组 [@eL type, value] <br> [@eL 1, 10086] <br> [@eL 1, null] |
ed | extDouble | 14 | 带类型标签的double | {<br> int32 type;<br> double value;<br> bool hasVal; <br>} | 格式固定二元数组 [@ed type, value] <br> [@ed 1, 0.5] <br>[@ed 1, null] |
es | extString | 15 | 带类型标签的string | {<br> int32 type;<br> string value; <br>} | 格式固定二元数组 [@es type, value] <br> [@es 10, @sL ^[\u4e00-\u9fa5_a-zA-Z0-9]+$<br/>] |
ptr | pointer | 18 | 指针 | {<br> string namespace;<br> string localId;<br> int32 type; <br> int32 policy; <br>} | 格式为单值 '@ptr localId' 格式或 object格式 <br/> @ptr 1001 <br> {@ptr ns: wjybxx, localId: 10001, type: 0} |
lptr | lite pointer | 19 | 轻量指针 | {<br> string namespace;<br> int64 localId;<br> int32 type; <br> int32 policy; <br>} | 格式为单值 '@lptr localId' 格式或 object格式 <br/> @lptr 1001 <br> {@lptr ns: wjybxx, localId: 10001, type: 0} |
dt | datetime | 20 | 日期时间 | { <br> int64 seconds; <br> int32 nanos;<br> int32 offset;<br> int32 enables; <br> } | 单值或object结构<br/> @dt 2023-06-17T18:37:00 <br/>{@dt date: 2023-06-17, time: 18:37:00, offset: +08:00, millis: 100} |
ts | timestamp | 21 | 时间戳 | { <br> int64 seconds; <br> int32 nanos;<br> } | 单值或object结构<br/> @ts 1715659200 <br/>{@ts seconds: 1715659200, nanos: 100} |
header | 29 | 对象头 | 对象形式: @{clsName: Vector3 } <br/> 简写形式: @{Vector3} | ||
array | 30 | 数组 | [ 1, 2, 3, 4, 5 ] | ||
object | 31 | 对象/结构体 | { name: wjybxx, age: 28 } |
特殊类型
特殊类型不是真正的类型,而是对类型的修饰,是语法糖,用以简化书写。
标签 | 类型 | 含义 | 格式或示例 |
---|---|---|---|
sL | string line | 单行纯文本 <br/>1. 单行有效,行尾结束 <br>2. 不会对字符进行转义,所见即所得<br/>3. 用于简化书写 | { <br/>url: @sL https://github.com/hl845740757<br/>} |
Dson规范
全局规范
- Dson是基于行解析的,换行符只支持
\n
和\r\n
。 //
双斜杠表示注释,单行有效。- 标签与被修饰的元素之间必须通过一个空格或换行分隔。
- 顶层对象必须是Object/Array/Header,你不可以直接声明一个基础值类型,顶层对象之间可以不使用逗号分隔。
- Dson大小写敏感。
- Json是有效的Dson输入。
ps: 我去除了顶层不能是header的限制,因此可以用顶层的header来表达文件头,用作其它目的都是不好的。
Number系列
- 简单数字格式以外的任何格式,都必须声明类型标签(含下划线时也是需要的)
- NaN、Infinity、-Infinity的支持取决于编程语言 —— 我现在不清楚是否有语言不支持
- int32和int64支持16进制和2进制,16进制以0x或0X开头,2进制以0b或0B开头;支持负号。
- 浮点数支持科学计数法。
- 整数和浮点数都支持下划线分割,不限制分割方式,但建议3个一组。
- 首尾不可以是下划线
- 不可以连续多个,下划线也不可以和小数点连续。
- 如无特殊说明,内置结构中的number都遵循这里的规范。
{
value1: 10001,
value2: 1.05,
value3: @i 0xFF,
value4: @i 0b10010001,
value5: @i 100_000_000,
value6: @d 1.05E-15,
value7: @d Infinity,
value8: @d NaN,
value9: @i -0xFF,
value10: @i -0b10010001,
value11: @d -1.05E-15,
}
bool值
- 未声明类型的情况下,只支持 true和false 两个值,且大小写严格;
- 声明类型的情况下,支持 0和1 输入; 1表示true,0表示false;
- 声明类型的情况下,拼写错误将引发错误 -- null同理。
{value1: true, value2: false, value3: @b 1, value4: @b 0}
# 拼写错误将引发异常
{value1: @b ture, value2: @N nul}
字符串(s、sL)
- 可以使用@s或双引号强调值是一个字符串类型 —— 更推荐使用双引号强调。
- 无引号字符串遇见换行符、不安全字符时将判定为结束;安全字符较多,我们给出不安全字符集。
- 存在一些特殊含义的字符串,用户最好总是使用双引号强调是字符串,否则可能发生兼容性问题。
引号字符串
可以使用双引号强调值为字符串类型,双引号会执行转义。
- 使用双引号时,反斜杠
\
是转义字符,解析时将对引号进行转义,因此内容中出现双引号时需要转义; - 使用双引号时,换行需要显式使用
\n
。 - 双引号字符串换行不可缩进。
简单文本块
简单文本块通过三个引号"""
来匹配。规则如下:
- 开始和结束的三个
"""
必须位于新行,这两行不属于内容行。 - 文本块开始的
"""
确定整体的缩进,此后的每一行至少具备相同的缩进。 - 简单文本块不支持转义,因此内容行的开始应避免出现
"""
。 - 简单文本框不支持行的拆分和合并。
- 简单文本框不支持注释。
PS: 可类比markdown的代码块格式。
// 第一个引号确定缩进,此后的每一行至少具备相同的缩进;不执行转义,保留原始文本
"""
我是wjybxx,是一个游戏开发者,Dson是我设计的文档型数据表达法,
你可以通过github联系到我。
thanks
"""
Dson文本块
简单的文本块仍然存在引号的转义和单行过长问题,因此Dson还设计了基于行首分割的文本块。
Dson风格的文本块通过@"""
来匹配,规则如下:
- 起始的三引号
@"""
后必须立即换行。 - 内容行通过行首标记内容开始,只有第一个空格是缩进,行首包括
@-
、@|
、@^
,详情见下表。 - 内容行不支持行尾注释;非内容行可以是注释。
PS: 虽然Dson文本块不要求行首@
对齐,也不要求开始和结束引号对齐,但建议总是对齐,以方便阅读。
// 相对简单文本块,使用 @""" 开始,结束符相同
@"""
@- 我是wjybxx,是一个游戏开发者,Dson是我设计的文档型数据表达法,
@- 你可以通过github联系到我。
@| thanks
@"""
文本块行首
标签 | 备注 | 类型 | 含义 | 格式或示例 |
---|---|---|---|---|
- | 连字符 | append | 合并行,上一行的换行符无效 | @- 这里续写上一行 |
| | 竖线 | append line | 普通行,上一行换行符有效 | @| 这是开启新的一行 |
^ | 亦或 | switch mode | 模式切换,插入转义行;<br/>上一行换行符无效 | @- 我想插入大量换行符<br/>@^ \n\n\n\n\n\n\n\n\n\n<br/>@- 接着书写 |
不安全字符
- 空白字符
- dson token字符集:花括号
{}
、方括号[]
、引号"
、逗号,
、冒号:
、艾特@
、斜杆/
、反斜杠\
- 特殊含义字符串:"true", "false", "null", "undefine", "NaN", "Infinity", "-Infinity"
- 简单数字(整数和小数)
转义字符
转义字符与json基本一致,但 '/' 无需转义。
引号 | 反斜杠 | 退格(BS) | 换页(FF) | 换行(LF) | 回车(CR) | 水平制表(HT) | unicode字符 |
---|---|---|---|---|---|---|---|
\" | \\ | \b | \f | \n | \r | \t | \u |
注意:反斜杠\
与要转义的字符必须连续,中途不可以换行。
二进制(bin)
- 配置格式限定二元组
[type, data]
- type限定为int32,且禁止负数
- data允许null,使用null时必须显式输入
- data部分使用 16进制 编码
[@bin 0, ABCD]
[@bin 0, null] // 支持null
扩展类型(ei、eL、ed、es)
- 配置格式限定二元组
[type, value]
- type限定int32,且禁止负数
- value部分遵循各自的规范
- 允许value为null,使用null时必须显式输入
[@ei 1, 1]
[@ei 1, null] // 支持null
指针(ptr)
- 指针支持两种范式
@ptr localId
和{@ptr localId: $localId, ns: $ns, type: $type, policy: $policy}
@ptr localId
简写方式适用大多数情况,结构体用于复杂情况。- ns是namespace的缩写,localId和namespace限定字符串,无特殊符号时可省略引号。
- Dson默认不解析指针,只存储为ptr结构,会提供根据localId解析的简单方法。
- 字段拼写错误将引发异常。
@ptr 10001
{@ptr localId: wjybxx001, ns : global, type: 1 }
{@ptr localId: wjybxx001, ns : global, type: 1, policy: 1}
PS:对于配置文件,指针的最大作用是复用和减少嵌套。
轻量指针(lptr)
轻量指针(lptr)是指针(ptr)的特化版,它们的结构体和语法基本一致,唯一的区别就是localId
为int64
类型,
int64类型的localId的适用性不如字符串的localId广,但在特定的领域下可以极大的减少指针带来的开销。
- lptr支持两种范式
@lptr localId
和{@lptr localId: $localId, ns: $ns, type: $type, policy: $policy}
@lptr localId
简写方式适用大多数情况,结构体用于复杂情况。- Dson默认不解析指针,只存储为ptr结构。
- 字段拼写错误将引发异常。
@lptr 10001
{@lptr localId: 10001, ns : global, type: 1 }
{@lptr localId: 10001, ns : global, type: 1, policy: 1}
日期时间(dt)
- dt支持两种范式
@dt datetime
和{@dt date: yyyy-MM-dd, time: HH:mm:ss, offset: ±HH:mm:ss, millis: $millis, nanos: $nanos}
@dt datetime
简写方式用于一般情况,datetime为 "yyyy-MM-ddTHH:mm:ss"格式,即ISO8601格式- date 部分为 "yyyy-MM-dd" 格式。
- time 部分为 "HH:mm:ss" 格式,不可省略秒部分。
- offset 部分为 "Z", "±H", "±HH", "±HH:mm" 或 "±HH:mm:ss" 格式,不可以省略正负号。
- millis 表示输入毫秒转纳秒 —— 简化书写。
- nanos 表示直接输入纳秒,millis 和 nanos通常只应该出现一个。
- date 和 time 至少输入一个,其它属性都是可选的,拼写错误将引发异常。
@dt 2023-06-17T18:37:00
{@dt date: 2023-06-17, time: 18:37:00}
{@dt date: 2023-06-17, time: 18:37:00, offset: +8}
{@dt date: 2023-06-17, time: 18:37:00, offset: +08:00, millis: 100}
{@dt date: 2023-06-17, time: 18:37:00, offset: +08:00, nanos: 100_000_000}
关于内置结构体的说明
- seconds 是纪元时间的秒时间(datetime)
- nanos 是时间戳的纳秒部分
- offset 是时区的秒数
- enables 记录了用户输入了哪些部分,由掩码构成,后4个比特位有效。
- date 的掩码是 0001
- time 的掩码是 0010
- offset 的掩码是 0100
- nanos 的掩码是 1000
时间戳(ts)
- dt支持两种范式
@ts seconds
和{@ts seconds: $seconds, millis: $millis, nanos: $nanos}
@ts seconds
简写方式用于一般情况,结构体用于复杂情况。@ts seconds
支持毫秒时间戳,以ms
结尾表示毫秒时间戳。- millis 表示输入毫秒转纳秒 —— 简化书写。
- nanos 表示直接输入纳秒,millis 和 nanos通常只应该出现一个。
@ts 1715659200
@ts 1715659200100ms // ms结尾表示毫秒时间戳
{@ts seconds: 1715659200, millis: 100}
object
- 键值对之间通过 ',' (英文逗号) 分隔。
- key和value之间通过 ':' (英文冒号)分隔,冒号两边空格分隔key和value不是必须的,但冒号和value之间建议输入空格。
- key为字符串类型,适用字符串规范,无特殊字符时可省略双引号,也可以使用 @ss 标签。
@{}
用于声明对象的header信息 -- 新版本不再要求’@‘必须与'['紧邻。- 允许末尾出现逗号
array
- value之间通过 ',' (英文逗号) 分隔。
@{}
用于声明数组的header信息 -- 新版本不再要求’@‘必须与'['紧邻。- 允许末尾出现逗号
header
- header支持两种范式,"@{clsName}" 和 "@{k1:v1, k2:v2}"。
- "@{clsName}" 是 @{clsName: $clsName}的语法糖,用于一般情况下简化书写。
- clsName为字符串类型,适用字符串规范,无特殊字符时可不加引号。
- header结构体不可以再内嵌header
- header没有默认结构体,以允许用户自行扩展。
以下是object声明header的示例
// 不声明header
{ x: 0, y: 0, z: 0 }
// 只声明ClassName
{@{Vector3} x: 0, y: 0, z: 0 }
// 声明复杂的header
{@{clsName: Vector3, localId: 321123} x: 1, y: 1, z: 1}
${clsName}特殊支持
在Dson2.1中,我们对@{clsName}做了优化,在无引号字符串模式下,可通过}
匹配结束。
这个优化来源于泛型的序列化和反序列化需求,泛型类的clsName包含[]
这俩特殊字符,
这导致我们的clsName常常需要使用双引号"
包起来,为简化大多数情况下的文本编写和提高可读性,我们对其做特殊的语法支持。
# 以下两种的方式等价
[@{List[V3]}
{x: 1, y: 1.5, z: 1},
{x: 2, y: 2.5, z: 2}
]
[@{"List[V3]"}
{x: 1, y: 1.5, z: 1},
{x: 2, y: 2.5, z: 2}
]
Header特殊属性依赖
虽然header没有默认结构体,但我们还是依赖了一些属性,以支持一些基础功能。因此,如果用户扩展header,使用了以下属性名,请确保类型一致,否则可能引发兼容性问题。
属性名 | 类型 | 含义 | 备注 |
---|---|---|---|
clsName | string | ClassName的缩写,表达当前对象的类型 | 可包括内置基础值和内置结构体 |
compClsName | string | ComponentClassName的缩写,表示数组成员或字典的value类型 | 可包括内置基础值和内置结构体 |
keyClsName | string | KeyClassName的缩写,表示字典类型的Key的类型 | 可包括内置基础值和内置结构体 |
localId | string/int64 | 对象本地id | 用于支持默认的引用解析 |
ps:
- keyClsName和compClsName用于非泛型情况下的类型自解释 —— 用于跨语言序列化。
- 如果语言支持泛型,可以只使用clsName记录类型的完整信息 —— 用于单语言序列化。
- clsName不可以包含冒号
:
和大括号{}
,
无类型value解析规则
- 首先去掉value两端的空白字符
- 如果value首字符为 { ,则按照object解析
- 如果value首字符为 [ ,则按照array解析
- 如果value首字符为 " ,则按照字符串解析
- 如果value为 true 或 false ,则解析为bool类型
- 如果value为 null,则解析为null
- 如果value匹配整数或浮点数(特殊格式不匹配),则固定解析为double类型。
- 其它情况下默认解析为String
一些好的实践
- 顶级对象之间通过空内容行分离
- key尽量遵守一般编程语言变量命名规范,仅使用数字字母和下划线,且非数字开头,以避免引号;同时key应当保持简短,避免换行。
- ClassName尽量不包含特殊字符,使得ClassName总是可以无引号表达。可考虑通过点号'.'或下划线'_'分隔。
- Object和Array的header信息
@{}
总是与\[
和{
紧邻。 - header的定义要保持简单和简短,避免增加额外的复杂度。
- 字符串尽量使用无引号模式或单行纯文本,手写转义是麻烦易出错的工作。
- 文本块总是通过行首左对齐。
- 一旦你想以某种特殊形式输入一个值,都应该显式声明这个值的类型。
- 数字避免使用8进制,浮点数慎用16进制
PS:内置结构体的值类型都是确定的,因此可以不声明类型直接使用特殊方式输入。
高级概念
集合
在Dson标准中,允许一个文件中包含多段Dson文本,每段文本表达一个Object或Array; 在这种情况下,我们将整个文件看做一个小型数据库,一个单表或单集合的数据库; 由于每段文本的结构可能不一致,这里我们选择NoSql概念下的集合。
因此,在Dson库中读写整个文件的接口都包含Collection
,而读写文件中单个值的接口则不包含。
投影
投影(Projection)是指在解析Dson文本时,只选择性地返回某些字段,而不是返回整个Dson文档 —— 类似MongoDB中的投影。
目标
配置文件的常见需求之一便是:获取配置的某个部分。Dson既然要作为配置文件,自然要支持这个需求。
值得一提的是,Reader和Scanner为投影做了原生支持,会尽量避免解析不必要的字段(token),而不是先完整解析后再投影,因此是高效的。
语法规则
- $header 表示投影对象的header,header总是全量投影;header默认不返回,只有显式指定的情况下返回;
- value为1表示选择,为0表示排除;全为0时表示反选模式,否则只返回value为1的字段 -- header不计入。
- $all 用于选择object的所有字段,强制为反选字段模式;主要解决声明header的影响,也方便进入排除模式。
- 如果无法根据投影信息确定投影值的类型,将由真实的数据决定返回值类型 -- 可用于测试数据类型。
- $slice 表示数组范围投影,对数组进行细粒度投影时必须声明$slice,否则返回空数组。
- $slice skip 表示跳过skip个元素,截取剩余部分;兼容 $slice [skip]输入;
- $slice [skip, count] 表示跳过skip个元素,截取指定个数的元素部分;
- $elem 表示数组元素进行投影。
- Object的投影为Object,Array的投影为Array。
- 点号'.'默认不是路径分隔符,需要快捷语法时需要用户自行定义。
语法示例
{
name: 1,
age: 0, // 不返回age字段
pos: {
$header: 1, // 返回pos的header
$all: 1, // 返回pos的全部字段 -- 可表示这是一个object映射
z: 0 // 排除z
},
arr1: {
$slice: 1, // $slice 用于对数组进行切片投影
$elem: { // $elem 用于对数组元素进行投影
name: 1,
pos: 1
}
},
arr2: {$slice: 1}, // 跳过1个元素,选择剩余所有元素
arr3: {$slice: [0, 5]}, // 选择数组的前5个元素、
arr4: 1, // 返回arr4整个数组
key1: {}, // 如果key1存在,则返回对应空Object/空Array。
key2: {$header: 1}, // // 如果key2存在,返回的空Object或空Array将包含header。
}
PS:测试用例见ProjectionTest.java
。
多语言
作者本人是一个游戏开发者,熟悉JAVA、C#、LUA,后期会提供C#的Dson实现;其它语言可能只能依靠喜欢上Dson的小伙伴们提供。
-
# java maven坐标(最新可查看Mvn中央仓库) <groupId>cn.wjybxx.dson</groupId> <artifactId>dson-core</artifactId> <version>2.1.2</version>
nuget Wjybxx.Dson 2.1.2
与其他配置文件格式的比较
JSON和Bson
json存在的问题:
- 不支持注释。
- 不支持注释导致Json不适合做配置模板,虽然部分库支持注释,但并不是所有库都支持。
- 字符串不支持换行。
- 程序生成的json文件经常出现一行上千和上万个字符的情况。
- 手写json出现长文本时非常难受。
- 不支持文本模式,不支持所见即所得,要处理大量的转义字符。
- json字符串存在引号的时候,转义的问题更加明显。
- 总是要加引号,key要加,value要加,手写json的时候有点难受。
- 值的精确程度不够,json当初是为js脚本语言设计的。
bson存在的问题:
- bson最大的问题是兼容json,它包含了json的所有问题 —— 我觉得这是bson最大的失误。
- bson是为mongodb数据库设计的,其数据格式与类型与数据库强相关。
- bson的类型语法设计得有点复杂,因为它是基于标准的json格式设计出来。
YAML
我承认,初次看见YAML文件的时候感到很优美,比如unity生成的yaml文件。 但研究yaml的规则之后,我对于YAML的流行表示不解,我觉得yaml的流行是一种灾难。
虽然我讨厌写json,但我认为json设计的其实很优美,它非常简单,输入严格,你很容易学会使用它。
YAML看上去也很简单是不是?但YAML的简单只是表面的简单。
- 使用空格缩进来表示层级关系,这是YAML最大的优点和缺点。
- 使用不可见的空格缩进来表示层级关系,如果YAML是程序生成的,那很beautiful。
- 如果YAML是手工书写,并可能长期维护的,这就是灾难 —— 文件越大,嵌套越深,越痛苦。
- 个人认为缩进不应该影响正确性,这非常脆弱,多方面的脆弱。
- YAML换行处理治标不治本
- YAML提供了好多种符号来处理文本段换行问题,在我看来就是没有方案,因为各个都不彻底。
- YAML不易扩展,因为YAML的规则太复杂了。
- 写个无bug的Json解析工具可能一天,写个无bug的YAML解析工具呢?
YAML的设计哲学里忽略了一点,需要手工编写的文件,维护成本才是最重要的! 美观最多放第二。
其实json的可读性我觉得已经足够好了,yaml的排版虽然漂亮,但付出的代价太大 —— 我写个文件就是为了排版好看吗?
PS:
- 我经常怀疑YAML的设计者是不是程序员...
- 输入并不是越少越好,简化输入不能损害精确性
TOML
说实话,TOML我接触的很少,看了几个toml文件示例后,我觉得它太像properties文件的优化版了。 TOML的语法非常接近编程语言:
- 可通过点号 . 来获取属性和赋值;也支持了数据结构内联
- 字符串段落使用三个引号""" —— 现在编程语言流行这样,Java也支持;但我想彻底摆脱转义。
- 换行继续输入使用反斜杠+换行 —— 说起来我最开始也想过这个方案,使用shell的经验
不过,我的直觉是TOML并不适合编写复杂的配置文件,更不适合用作程序导出的文件格式,所以我不选择TOML。
PS:
- 支持数字使用下划线,来自TOML的提醒,我在写代码时经常用到。
- TOML语法也是比较独特的,Dson也是。
- 还有一个我比较陌生的配置文件格式 HOCON,但HOCON只适合程序员,因为太像脚本语言。
Dson呢?
Dson是一个看似复杂,实则简单的语言。
Dson主要解决的问题有三个:
- 单行过长问题 - 期望能换行继续输入
- 纯文本和转义问题 - 转义很影响可读性
- 序列化的精确性问题 - Dson最初是为序列化设计的
前两个问题,通过忽略换行符,使用\n
和纯文本行标签得到了很好的解决。
序列化的精确性问题则通过对象头来解决;对象头是额外的数据区,不会污染正常的数据区; 我们通过在对象头中存储对象的类型信息,可以实现序列化的精确性,也可以实现一些其它的扩展功能。
特别鸣谢
这里感谢MongoDB的Bson团队,Bson源代码对于我实现Dson起到了很大的帮助作用。
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net6.0
- Wjybxx.Commons.Core (>= 1.0.13)
-
net7.0
- Wjybxx.Commons.Core (>= 1.0.13)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated | |
---|---|---|---|
2.2.0-rc3 | 136 | 7/31/2024 | |
2.2.0-rc2 | 152 | 7/21/2024 | |
2.2.0-rc1 | 147 | 7/14/2024 | |
2.1.2 | 146 | 5/20/2024 | |
2.1.1 | 147 | 5/16/2024 | |
2.1.0 | 157 | 5/15/2024 | |
2.0.2 | 166 | 5/4/2024 | |
2.0.1 | 132 | 4/9/2024 | |
2.0.0 | 153 | 3/30/2024 | |
1.4.1 | 175 | 1/7/2024 | |
1.4.0 | 154 | 1/6/2024 | |
1.4.0-rc2 | 149 | 1/2/2024 | |
1.4.0-rc1 | 142 | 12/31/2023 | |
1.3.0 | 173 | 12/30/2023 | |
1.2.0 | 162 | 12/27/2023 | |
1.0.0 | 159 | 12/26/2023 | |
1.0.0-rc2 | 154 | 12/25/2023 | |
1.0.0-rc1 | 152 | 12/23/2023 |