GoWeb开发定制JSON序列化

高级JSON定制化

通过使用结构体标签、添加空白和封装响应数据,我们已经能够为JSON响应添加大量定制信息。但是,当这些内容还不够时,您需要更自由地定制JSON时,会发生什么呢?

要回答这个问题,我们首先需要谈谈Go如何处理JSON序列化的一些理论。要理解的关键是:

Go是在什么时候将特殊类型序列化为JSON,它首先查看对应的类型是否实现了MarshalJSON()方法。如果实现了,GO将调用这个方法来决定JSON编码格式。

这么讲有点模糊,我们更精确点。严格地说,当Go将特定类型编码为JSON时,它会查看该类型是否满足json.Marshaler接口,该接口如下所示:

typeMarshalerinterface{MarshalJSON()([]byte,error)}

如果类型确实满足接口,那么Go将调用它的MarshalJSON()方法,并使用它返回的[]byte切片作为JSON编码的值。

如果该类型没有MarshalJSON()方法,那么Go将返回尝试根据自己的内部规则将其编码为JSON。

因此,如果我们想定制某些类型的编码方式,只需要在其上实现MarshalJSON()方法,该方法以[]byte类型返回自定义的JSON内容。

提示:如果您查看time.Time类型源代码,就可以看到这一点。time.Time实际上是一个结构体,但是它有一个MarshalJSON()方法,输出RFC格式JSON对象。当time.Time值被序列化为JSON对象时,就会调用MarshalJSON()方法。

定制电影Runtime字段JSON序列化

为了说明这一点,让我们看一下应用程序中的一个具体事例。

当我们的Movie结构被编码为JSON时,Runtime字段(它是一个int32类型)编码为JSON数字。现在我们来更改它,将其编码为"runtimemins“的字符串。像这样:

{"id":,"title":"Casablanca","runtime":"mins","genres":["drama","romance","war"],"version":1}

有几种方法可以实现这一点,但一种简单的方法是为Runtime字段创建一个自定义类型,并在这个类型上实现MarshalJSON()方法。

为了防止internal/data/movie.go文件不会太乱,我们创建一个新的文件来处理runtime类型序列化逻辑:

$touchinternal/data/runtime.go

然后继续添加以下代码:

packagedataimport("fmt""strconv")//申明Runtime类型,其底层是int32类型(和movie中的字段一样)typeRuntimeint32//实现MarshalJSON()方法,这样就实现了json.Marshaler接口。func(rRuntime)MarshalJSON()([]byte,error){//生成一个字符串包含电影时长jsonValue:=fmt.Sprintf("%dmins",r)//使用strconv.Quote()函数封装双引号。为了在JSON中以字符串对象输出,需要用双引号。quotedJSONValue:=strconv.Quote(jsonValue)//将字符串转为[]byte返回return[]byte(quotedJSONValue)}

这里我想强调两点:

如果您的MarshalJSON()方法向我们的方法一样返回一个JSON字符串值,那么您必须在返回字符串之前用双引号包装它。否则它将不会被解释为JSON字符串,你将收到类似于这样的运行时错误:

json:errorcallingMarshalJSONfortypedata.Runtime:invalidcharactermaftertop-levelvalue

我们故意为MarshalJSON()方法使用值接收器,而不是指针接收器func(r*Runtime)MarshalJSON()。这给了我们更多的灵活性,因为这意味着定制JSON编码将对Runtime值对象和指针对象都有效。

好的,现在有了自定义Runtime类型,打开internal/data/movies.go文件并更新Movie结构:

File:internal/data/movies.go

packagedataimport("time")typeMoviestruct{IDint64`json:"id"`CreateAttime.Time`json:"-"`Titlestring`json:"title"`Yearint32`json:"year,omitempty"`//使用Runtime类型取代int32,注意omitempty还是能生效的RuntimeRuntime`json:"runtime,omitempty,string"`Genres[]string`json:"genres,omitempty"`Versionint32`json:"version"`}

重启服务然后对GET/v1/movies/:id接口发起请求。你应该看到一个包含自定义runtime值的响应,格式为"xxmins",类似如下:

$curllocalhost:/v1/movies/{"movie":{:,:,:,:[,,],:1}}

总之,这是定制JSON序列化的一种很好的方法。我们的代码简洁明了,并且我们有一个自定义的Runtime类型,可以随时随地使用它。

但也有不利的一面。在将代码与其他包集成时,使用自定义类型有时会很尴尬,您可能需要执行类型转换,将自定义类型转换为其他包理解和可接受的值。




转载请注明:http://www.guyukameng.com/aspnet/aspnet/2023-04-21/16691.html

  • 上一篇文章:
  •   
  • 下一篇文章: 没有了