GO JSON 的序列化与反序列化

golang json marshal and unmarshal

foreversmart write on 2021-03-21
在 Golang 中对于 json 的序列化和反序列化的控制大概有三种方式
在默认的 JSON TAG 加上控制标签
实现MarshalJSON() ([]byte, error)和UnmarshalJSON(b []byte) error 覆盖默认的 JSON 序列化和反序列化方法
利用反射解析 Struct 字段自定义实现一个JSON 序列化和反序列化的功能
下面重点看下前两个比较简单的 JSON 序列化和反序列化的方法,第三个通过反射实现的序列化和反序列化的方法比较复杂且适用于自定义需求特别强的场景。
序列化
通过 Golang struct 的 tag 我们可以对例子中的 Student 模型类实现一些个性化的序列化需求
控制一个字段的值是空值的时候,序列化的结果中不包含这个字符串
控制一个字段在序列化的时候直接不被序列化
控制一个字段序列化为其它的格式
控制一个字段在不同的值范围序列化为不同的形式
根据已有的字段序列化输出新的字段
对于前两个需求在 golang 中我们可以简单的通过在 tag 上增加一些标记来实现,分别是`json:",omitempty"` `json:"-"`
下面我们着重来看下后面的几个需求
首先假设我们有一个这样的模型类:
Go
控制一个字段序列化为其它的格式
现在我们需要将 Student 中的 Age 年龄字段序列化为字符串的形式
我们可以在 Student 上定义 MarshalJSON() ([]byte, error) 方法
由于 Student 上 Age 的字段是 int 但是我们序列化输出的是 string 所以我们必须要定义一个中间 struct 方便序列化 Age 是 string,当然也可以使用 Map 但是过于繁杂,我们需要将一个个字段取出来放进 map 里面。在定义中间 struct 的时候我们为了不重复的的申明多个字段,我们可以匿名组合原有的类型, 在中间类型上将 Age 变为 string 覆盖Student 中的 Age 字段。
Go
但是此时有一个问题我们在 Student 上定义了一个 MarshalJSON() ([]byte, error) 方法,在这个方法中我们用到了一个中间 struct Temp 他匿名组合了 Student
在 json.Marshal Temp 的过程中会调用 Temp 上的 MarshalJSON 方法,而这个方法就是 Student 上的 MarshalJSON 所以会导致循环调用。
我们可以利用 Golang 中用一个类型定义一个新的类型的办法,定义中间类型 T 这个 T 类型只会继承 Student 的所有字段而不会继承他的方法。这个时候我们在匿名组合定义我们序列化需要的中间类型 Temp
Go
控制一个字段在不同的值范围序列化为不同的形式
这种场景相对较少,我们还是以 Student 中的 Age 字段为例,假设当年龄处于非法值的时候我们不输出这个字段,这里非法值的界定是 age < 0 和 age > 100, 当 age < 10 时我们输出 0 的 float 值。当 age ≥ 10 并且 age < 20 时输出 age 的 int 值,其它正常值输出 age 的整数字符串加上 "years old"。
整体的实现思路和上面类似增加了多个中间 struct 来实现不同的功能
Go
根据已有的字段序列化输出新的字段
也是对于 Student 这个模型类我们现在需要对里面的 Class 和 Grade 两个字段整合成一个复杂的结果就是学生的年级班级信息。
思路同上我们需要定义中间 struct Temp 在里面定义新的字段 GradeClassInfo
Go
如果我们不想看到原有的 grade 和 class 字段的序列化结果可以 json:"-" 屏蔽掉
Go
反序列化:
正常情况我们需要自定义反序列化的情况比较少,但实际开发的过程中还是有一些情况会用到。在 OpenStack 的 API 接口中就有不少这类的情况。我遇到的情况主要有以下种情况:
JSON 字符串和目标字段类型不同
给一个字段在反序列化的时候添加非空默认值
对于一个字段在不同的值会有多个类型
极其不规范的嵌套数据,甚至里面的字符串并非是一个标准的 JSON string
下面我们具体来看下这些情况怎么样去反序列化会比较优雅
JSON 字符串和目标字段类型不同
这事一个非常常见的需求,经常我们定义的 struct 模型和要反序列化的 JSON string 在某些字段上有微小类型差别,大概率是服务端版本问题导致的。
还是以 Student 模型为例实现这个需求我们需要在 Student 上增加一个 UnmarshalJSON(data []byte) error 方法或在他们的某些字段上增加一个 UnmarshalJSON(data []byte) error 方法。
比如现在我们需要对下面的 JSON string 反序列化为 Student 模型
Go
这里 Age 字段在字符串中是 string 类型而在 Student 中是 float64 类型,如果我们想保持现有的业务逻辑不变我们应该怎么进行处理呢?

「真诚赞赏,手留余香」

Foreversmart

真诚赞赏,手留余香

使用微信扫描二维码完成支付