同一个数据类型,同一个序列化框架内,由于关注点的不同,也会有不同的序列化方式。比如Date,可以被序列化成纯数值,也可以序列化成可读的字面值。
ISO-8601[1]对各种时间类型数值的序列化做出详尽的规范。各种编程语言的官方支持也很全面。示例格式如下:
2022-03-11T12:25:59.123Z
时间长度,对开始与结束时间点没有限定,如,一秒:
PT1S
表示固定的开始与结束时间点范围内的时间区间。
2022-03-11T12:25:59.123Z/2022-03-12T12:25:59.123Z
金额表示具有特定货币单位的数量。JDK中并没有提供对金额的内置支持,但是JCP已经通过了JSR-354[2]并提供了相关的实现[3]。各种常见的序列化框架也有相应的支持。
一般而言,所有存储、计算及内部信息流转,需要保证足够的精度,避免不必要的舍入与截断。确保所有需要做舍入与截断的场景中所使用的策略一致。
与第三方系统对接时,要确认第三方系统所使用的舍入策略并做相应的内部处理。
只在数据最终展示时,才应当按对应货币的要求,对金额进行舍入。
根据不同的场景,枚举可以被序列化成数值或文本。数值的性能更好,文本的可读性与可扩展性更好(因为比较容易在中间插入新的值)。除对性能要求极高的场景(如高频交易,高并发的交易系统等)之外,一般建议序列化成文本值。同时,建议不要把枚举值的字面值,直接作为序列化时的文本[4],以免重构枚举值时影响数据的序列化。尽管这个问题在不少序列化框架中提供了解决方案。
比如下面的枚举:
enum class WeekDay { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }
你知道这个1,是周一还是周二吗?
{ "weekday": 1 }
想一下,如果这个结构体发到了Kafka上,然后你想把Monday
改成MONDAY
{ "weekday": “Monday” }
这样,既一目了然,又不妨碍重构。
{ "weekday": “MON” }
尽管在RPC框架中使用的比较普遍,但是并不建议将异常本身序列化。如果的确需要序列化异常,需要:
序列化,一般用于信息交互与存储。需要避免
一般情况下,序列化结构与被序列化的对象的结构是一致的。但是序列化对象,根据其使用场景不同,其序列化结果方面的考虑也是不同的。
序列化的结构有两大各类,一种是平坦化的,尽量把数据组织成单层<名-值>对的形式;一种是尽量按原始对象结构化的形式进行组织,允许嵌套。不同场景会有倾向性,但是一般不是强制性的要求。
一般用于展示层和存储层。
{
"firstname": "Hugo",
"lastname": "Gu",
"country": "China",
"address": "Shanghai"
}
一般用于服务间信息交互。
{
"name": {
"firstname": "Hugo",
"lastname": "Gu"
},
"location": {
"country": "China",
"address": "Shanghai"
}
}
当需要返回一组数据的时候,总是要把列表包含在一个结构中,而不要直接把列表作为返回体。返回列表,后期需要新加字段,那么一定会造成兼容性破坏,即原数据与新数据结构不兼容。但是值得注意的是,有些通信协议会为了实现特定功能,要求返回数据类型(如JSON RPC[5]),此时建议优先遵循现有规范,或在一致的前提下,与相关方约定新的规范。
{
"total": "6",
"data": ["item1", "item2"]
}
["item1", "item2"]
及
[{"name": "hugo", "age": 5}]
信息的名,放在Json的Key上,信息的值放在Json的Value上,这样也才便于数据的校验与解析。一般而言,JSON结构的Key,是固定的,只有Value才会有变化。
比如,考虑一个请求体验证函数所返回的验证结果,会给于有问题的请求体字段名和所出的问题。
{
"xpath": "request/username",
"error": "username shouldn't be empty"
}
这个JSON中,key和value,都被用来存储值了,都会变化。既不可读,也不便于解析。
{
"request/username": "username shouldn't be empty"
}