Go and JSON

在使用Go开发web项目的过程中, 数据库读写操作与JSON格式的输入输出是两块最基础的模块, Go的标准库已经帮我们做了很多, 熟悉database/sql与encoding/json这两个库能帮我们更自在地开发web应用.

但此篇文章抛开基础不说, 只说一些在开发中遇到一些真实存在的痛点.

如何处理Null值?

Go的一大特色就是zero value, 比如int类型的zero value是0, string为"", struct为每个field里各自类型的zero value. 因此在Go的很多ORM处理NULL值时, 都是通过zero value机制入库或出库的, 因此, 使用ORM操作的数据库, 如何没有明确指明, 基本上看不到NULL值. 一个可能为NULL的varchar字段, 存入了""(空字符串).

这里不讨论关于空字符串与NULL的优劣, 而是说明如何处理NULL值.

sql标准库里带了这么几个类型: ```sql.NullString, sql.NullInt64, sql.NullBool, sql.NullFloat64```, 如果在定义struct里field类型的时候, 使用sql.NullString代替string的话, 就可以在数据库里存入NULL值.

通过源码看到, NullString的结构与两个方法:

type NullString struct{
String string
Valid bool
} Scan(value interface{}) error
Value()(driver.Value, error)

Scan实现了sql.Scanner接口, Value实现了driver.Valuer接口. 这两个接口表示数据从Go端与DB端的转换. Scan用于数据从DB转到Go时被调用,也就是说select时用(查看源码); 而Value则由Go转到DB时被调用. 也就是说insert/update时候用(查看源码).

在两个不同领域做数据转换时, 通常用接口来定义, 这是接口的作用, JSON也是同样如此.

有了接口的帮助, 任何实现了这两个接口的类型, 都能在DB与Go之间做转换, 接口建立起了桥梁.

如何处理自定义类型?

由上一节可以想到, 如果在想要保存Go自定义类型到DB里, 只需要实现Scanner与Valuer接口, 就像sql.NullString一样.

典型如NullTime(查看源码):

type NullTime struct {
Time time.Time
Valid bool
} func (nt *NullTime) Scan(value interface{}) error {
nt.Time, nt.Valid = value.(time.Time)
return nil
} func (nt NullTime) Value() (driver.Value, error) {
if !nt.Valid {
return nil, nil
}
return nt.Time, nil
}

为了让这个类型更好用, 添加一个帮助函数:

func ToNullTime(t time.Time) NullTime {
return NullTime{Time: t, Valid: !t.IsZero()}
}

自定义类型如何转化成JSON?

如果拿一个sql.NullString的类型, 做json.Marshal, 那么得到输出是这样的:

{
...
"column_name" : { "String" : "Hello?", "Valid" : true },
...
}
//而我们想要的是:
{
...
"column_name" : "Hello?",
...
}
//or
{
...
"column_name" : null,
...
}

我们知道, json库有两个标准的接口, json.Marshaler(查看源码) , json.Unmarshaler(查看源码), 如果一个类型实现此两个接口, 则在使用json.Marshal/Unmarshal调用时, 调用此类型的自定义方法:

type NullString struct {
sql.NullString
} func(v *NullString) MarshalJSON() ([]byte, error) {
if v.Valid {
return json.Marshal(v.String)
} else {
return json.Marshal(nil)
}
} func (v NullString) UnmarshalJSON(data []byte) error {
var s *string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
if s != nil {
v.Valid = true
v.String = *s
} else {
v.Valid = false
}
return nil
}

总结

Go语言的接口, 扮演了桥梁的角色, 连接起了Go与其它领域的数据转换, 通过实现标准库的接口, 我们可以让Go的数据类型轻松适应不同数据领域.

[参考]
http://dennissuratna.com/marshalling-nullable-string-db-value-to-json-in-go/

http://blog.carbonfive.com/2015/07/09/there-will-be-sql/

http://marcesher.com/2014/10/13/go-working-effectively-with-database-nulls/

Go and JSON的更多相关文章

  1. 使用TSQL查询和更新 JSON 数据

    JSON是一个非常流行的,用于数据交换的文本数据(textual data)格式,主要用于Web和移动应用程序中.JSON 使用“键/值对”(Key:Value pair)存储数据,能够表示嵌套键值对 ...

  2. 【疯狂造轮子-iOS】JSON转Model系列之二

    [疯狂造轮子-iOS]JSON转Model系列之二 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇<[疯狂造轮子-iOS]JSON转Model系列之一> ...

  3. 【疯狂造轮子-iOS】JSON转Model系列之一

    [疯狂造轮子-iOS]JSON转Model系列之一 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 之前一直看别人的源码,虽然对自己提升比较大,但毕竟不是自己写的,很容易遗 ...

  4. Taurus.MVC 2.2 开源发布:WebAPI 功能增强(请求跨域及Json转换)

    背景: 1:有用户反馈了关于跨域请求的问题. 2:有用户反馈了参数获取的问题. 3:JsonHelper的增强. 在综合上面的条件下,有了2.2版本的更新,也因此写了此文. 开源地址: https:/ ...

  5. .NET Core系列 : 2 、project.json 这葫芦里卖的什么药

    .NET Core系列 : 1..NET Core 环境搭建和命令行CLI入门 介绍了.NET Core环境,本文介绍.NET Core中最重要的一个配置文件project.json的相关内容.我们可 ...

  6. 一个粗心的Bug,JSON格式不规范导致AJAX错误

    一.事件回放  今天工作时碰到了一个奇怪的问题,这个问题很早很早以前也碰到过,不过没想到过这么久了竟然又栽在这里. 当时正在联调一个项目,由于后端没有提供数据接口,于是我直接本地建立了一个 json ...

  7. JSON.parse()和JSON.stringify()

    1.parse 用于从一个字符串中解析出json 对象.例如 var str='{"name":"cpf","age":"23&q ...

  8. json与JavaScript对象互换

    1,json字符串转化为JavaScript对象: 方法:JSON.parse(string) eg:var account = '{"name":"jaytan&quo ...

  9. .NET平台开源项目速览(18)C#平台JSON实体类生成器JSON C# Class Generator

    去年,我在一篇文章用原始方法解析复杂字符串,json一定要用JsonMapper么?中介绍了简单的JSON解析的问题,那种方法在当时的环境是非常方便的,因为不需要生成实体类,结构很容易解析.但随着业务 ...

  10. WebApi接口 - 响应输出xml和json

    格式化数据这东西,主要看需要的运用场景,今天和大家分享的是webapi格式化数据,这里面的例子主要是输出json和xml的格式数据,测试用例很接近实际常用情况:希望大家喜欢,也希望各位多多扫码支持和点 ...

随机推荐

  1. .Net SSRS(rdlc) 报表经验总结

    排版 1. 可以利用表格来布局,以避免调整固定宽度的麻烦. 2. 一个表的表头里还可以嵌套表格. 3. 设置rdlc报表打印格式.首先打开RDLC报表设计器页面.在灰色部分点右键 -> 报表属性 ...

  2. laravel跟jquery之间传输json数据

    laravel代码: public function test(){ $arr = ["test1"=>"1","test2"=> ...

  3. 【转】const的用法,特别是用在函数前面与后面的区别!

    在普通的非 const成员函数中,this的类型是一个指向类类型的 const指针.可以改变this所指向的值,但不能改变 this所保存的地址. 在 const成员函数中,this的类型是一个指向 ...

  4. VS2010 Command Prompt Error:Cannot determine the location of the VS Common Tools folder

    就在VS2010 Command Prompt 用vcvarsall.bat x64重新设置环境变量的时候,出现了标题中的错误.原因就在参考链接中 References: http://stackov ...

  5. 深入浅出Node.js (8) - 构建Web应用

    8.1 基础功能 8.1.1 请求方法 8.1.2 路径解析 8.1.3 查询字符串 8.1.4 Cookie 8.1.5 Session 8.1.6 缓存 8.1.7 Basic认证 8.2 数据上 ...

  6. 一个资深java面试官的“面试心得”

    在公司当技术面试官几年间,从应届生到工作十几年的应聘者都遇到过.先表达一下我自己对面试的观点: 1.笔试.面试去评价一个人肯定是不够准确的,了解一个人最准确的方式就是“路遥知马力,日久见人心”.通过一 ...

  7. 【转】如何删除一个repository(仓库)

    原文网址:http://my.oschina.net/anna153/blog/377758?p=1 如何删除自己创建的一个项目,我浏览了一下github网站,确实不太容易找到删除功能.这里介绍一下啊 ...

  8. 【转】JAVA中的浅拷贝和深拷贝

    原文网址:http://blog.bd17kaka.net/blog/2013/06/25/java-deep-copy/ JAVA中的浅拷贝和深拷贝(shallow copy and deep co ...

  9. poj1014:母函数+优化

    题目大意: 有1~6六种宝石,价格分别为1~6 ..给定每种宝石的个数,问能否平分给两个人 分析: 一看显然是个多重背包问题,也可以用母函数做 不过母函数的复杂度是n*v*k,第一次tle了.. 后来 ...

  10. WPF与输入法冲突研究之二:汉字输入法会导致WPF程序的崩溃!

    如果是输入非汉字的数据信息,可以添加一下内容: xmlns:input="clr-namespace:System.Windows.Input;assembly=PresentationCo ...