golang 中把struct 转成json格式输出

 package main

 import (
"encoding/json"
"fmt"
) type Person struct {
Name string `json:"name,omitempty"`
DoB string `json:"dob,omitempty"`
Age string `json:"-,omitempty"`
} type Singer struct {
Person
MusicGenre string `json:"music_genre,omitempty"`
Age string `json:"ageee,omitempty"`
} func ToJSON(s interface{}) (string, error) {
bs, err := json.Marshal(s)
if err != nil {
return "", err
}
// fmt.Println(bs)
return string(bs), nil
} func main() {
s := Singer{
Person{"John Singer",
"01-02-1975",
"13"},
"pop", "12" }
sJSON, _ := ToJSON(s)
fmt.Println(sJSON)
// {"name":"John Singer","dob":"01-02-1975","music_genre":"pop"}
}

  

---------------------------------

One of the things that I missed the most, apart from generics, when coming to Go while having Java background was the lack of inheritance. In JVM it was pretty simple, as you can define a parent and child classes, then have a common behavior present all the children objects. This is not the case in Go, and I had to learn how to work around this. Here, we don't have inheritance, we have the composition, and for that, we have a pretty useful technique called embedding structs.

Manual composition vs embedding

In the classical approach to composition, we add what we'd call a parent class as another field in the child one. In Go terms, we should first define a parent struct with common attributes:

type Person struct {
Name string
}

Then we should add an attribute of type Person to another struct which we want to use that common features from the parent:

type Singer struct {
Parent Person
MusicGenre string
}

This may not seem like a huge improvement, but we can use a shorter notation and embedPerson into Singer by skipping the attribute name and just leave its type:

type Singer struct {
Person
MusicGenre string
}

In both cases, when creating an instance of Singer inline we need to specify a definition of parent struct with its name - when embedding it is the name of that struct:

// manual composition
s0 := embedding.Singer{
Parent: embedding.Person{Name: "John Singer"},
MusicGenre: "pop",
} // embedding
s1 := embedding.Singer{
Person: embedding.Person{Name: "John Singer"},
MusicGenre: "pop",
}

The difference is larger than just saving a few characters in the struct definition. First of all, when doing it manually we'd have to use attribute name (Parent) all the time:

// manual composition
fmt.Println(s0.Parent.Name)

While with embedding we can (but don't have to) refer to the attributes of the embedded struct as if they were defined in the child:

// embedding
fmt.Println(s1.Parent.Name)
fmt.Println(s1.Name)

Another interesting thing is that if we want to build a Singer struct by adding attributes to an empty instance, again we get a feeling of the inheritance:

// manual composition
s0 := embedding.Singer{MusicGenre: "pop"}
s0.Parent.Name = "John Singer" // we have to do this explicitly
fmt.Println(s0.Parent.Name) // embedding
s1 := embedding.Singer{MusicGenre: "pop"}
// we can be explicit with
// s1.Person.Name = "John Doe"
// but we can as well do this:
s1.Name = "John Singer"
fmt.Println(s1.Parent.Name)

Calling inherited functions

Another useful feature of embedding structs is that we can call parent's functions that were inherited as if they were defined on a child struct. For example, we can add a Talk(..)function on Person and use it on an instance of Singer:

type Person struct {
...
} func (p Person) Talk(message string) {
fmt.Printf("%s (a person) says \"%s\"\n", p.Name, message)
} func main() {
s := Singer{}
s.Name = "John Doe"
s.Talk("Hello, reader!")
// John Doe (a person) says "Hello, reader!"
}

Awesome! Remember that the attributes and function are promoted to the child struct only if they are not overwritten there. If we define a Talk(..) function directly on Singer, the one from Person would never be called:

...
func (p Singer) Talk(message string) {
fmt.Printf("Singer %s says \"%s\"\n", p.Name, message)
}
func main() {
s := Singer{}
s.Name = "John Doe"
s.Talk("Hello, again!")
// Singer John Singer says "Hello again!"
}

The trick is when a function that is promoted calls another one. For example, if we define a Type() function that would return the struct identifier on both Person and Singer, then call it within Talk function that would be promoted from the parent, we would get the Type() from the parent as well. The reason for this is that at the time of executing the function, Go does not realize we are dealing with some inheritance stuff and we don't go back to the original caller to see what the context is:

func (p Person) Type() string {
return "PERSON"
}
func (p Person) Talk(message string) {
fmt.Printf("%s (type=%s) says \"%s\"\n", p.Name, p.Type(), message)
}
...
func (s Singer) Type() string {
return "SINGER"
} func (s Singer) Sing(title string) {
fmt.Printf("%s (type=%s) sings %s in the style of %s.\n", s.Name, s.Type(), title, s.MusicGenre)
}
...
func main() {
s := Singer{MusicGenre: "rock"}
s.Name = "Johny Singerra"
s.Sing("Welcome to the forest")
// Johny Singerra (type=SINGER) sings Welcome to the forest in the style of rock.
s.Talk("Hello!")
// Johny Singerra (type=PERSON) says "Hello!"
}

Hiding JSON properties

Another interesting fact is the way Go handler JSON tags that can identify attributes of a struct. For example, we can marshal Singer to JSON and see that both its own and the inherited properties end up in the document:

type Person struct {
Name string `json:"name,omitempty"`
DoB string `json:"dob,omitempty"`
}
...
type Singer struct {
Person
MusicGenre string `json:"music_genre,omitempty"`
}
...
func (s Singer) ToJSON() (string, error) {
bs, err := json.Marshal(s)
if err != nil {
return "", err
}
return string(bs), nil
}
...
func main() {
s := embedding.Singer{
Person: embedding.Person{
Name: "John Singer",
DoB: "01-02-1975",
},
MusicGenre: "pop",
}
sJSON, _ := s.ToJSON()
fmt.Println(sJSON)
// {"name":"John Singer","dob":"01-02-1975","music_genre":"pop"}
}

It's great that both name and dob properties are there, but what if we want to hide some of those for JSON marshaler? Let's create a MusicStar struct where we'll add a nickname to the Singer, but at the same time we'd like to hide the date of birth (since we want to keep it as a secret):

type MusicStar struct {
Singer
Nickname string `json:"nickname,omitempty"`
DoB string `json:"-,omitempty"`
} func (ms MusicStar) ToJSON() (string, error) {
bs, err := json.Marshal(ms)
if err != nil {
return "", err
}
return string(bs), nil
}

Note that we've added a DoB field but added a - as JSON tag to indicate that this field should be removed from marshaling. Unfortunately, that doesn't work:

func main() {
ms := embedding.MusicStar{
Nickname: "Starry",
Singer: embedding.Singer{
Person: embedding.Person{
Name: "Joe Star",
DoB: "01-02-1975",
},
MusicGenre: "pop",
},
}
msJSON, _ := ms.ToJSON()
fmt.Println(msJSON)
// "name":"Joe Star","dob":"01-02-1975","music_genre":"pop","nickname":"Starry"}
}

That is because although Go sees our - JSON tag, it recognizes it as undefined and go deeper into embedded structs to see if there is a field that matches the name, but has some concrete tag defined. It finds one, so that is being used. We can, however, trick the language into hiding that nested DoB, by defining the same property with the same JSON tag in the top-level struct. This way the DoB from Person will never be promoted to the top level JSON object, since its top-level value is empty, therefore the empty string overwrites anything that comes from Person:

type MusicStar struct {
Singer
Nickname string `json:"nickname,omitempty"`
DoB string `json:"dob,omitempty"`
}
...
msJSON, _ := ms.ToJSON()
fmt.Println(msJSON)
// {"name":"Joe Star","music_genre":"pop","nickname":"Starry"}

As you can see, embedded structs solve some of the things we would achieve via classical inheritance, but it's necessary to understand how it works to avoid unexpected behaviors and gotchas. The full source code of these examples is available on Github.

golang embedded structs的更多相关文章

  1. 从OOP的角度看Golang

    资料来源 https://github.com/luciotato/golang-notes/blob/master/OOP.md?hmsr=toutiao.io&utm_medium=tou ...

  2. golang Methods on structs

    原文:http://golangtutorials.blogspot.com/2011/06/methods-on-structs.html snmp 下载,有空学习一下! https://sourc ...

  3. Go语言(golang)开源项目大全

    转http://www.open-open.com/lib/view/open1396063913278.html内容目录Astronomy构建工具缓存云计算命令行选项解析器命令行工具压缩配置文件解析 ...

  4. [转]Go语言(golang)开源项目大全

    内容目录 Astronomy 构建工具 缓存 云计算 命令行选项解析器 命令行工具 压缩 配置文件解析器 控制台用户界面 加密 数据处理 数据结构 数据库和存储 开发工具 分布式/网格计算 文档 编辑 ...

  5. Golang优秀开源项目汇总, 10大流行Go语言开源项目, golang 开源项目全集(golang/go/wiki/Projects), GitHub上优秀的Go开源项目

    Golang优秀开源项目汇总(持续更新...)我把这个汇总放在github上了, 后面更新也会在github上更新. https://github.com/hackstoic/golang-open- ...

  6. golang sync.noCopy 类型 —— 初探 copylocks 与 empty struct

    问题引入 学习golang(v1.16)的 WaitGroup 代码时,看到了一处奇怪的用法,见下方类型定义: type WaitGroup struct { noCopy noCopy ... } ...

  7. Golang通脉之面向对象

    面向对象的三大特征: 封装:隐藏对象的属性和实现细节,仅对外提供公共访问方式 继承:使得子类具有父类的属性和方法或者重新定义.追加属性和方法等 多态:不同对象中同种行为的不同实现方式 Go并不是一个纯 ...

  8. 【转载】关于Embedded Linux启动的经典问题

    转载自:http://linux.chinaunix.net/techdoc/install/2009/04/13/1107608.shtml 发信人: armlinux (armlinux), 信区 ...

  9. [转]50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs

    http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/ 50 Shades of Go: Traps, Gotc ...

随机推荐

  1. HBuilder git使用教程

    1.插件安装 打开HBuilder,工具->插件安装. 等待安装,成功后提示重启后生效,立即重启. 2.在码云上新建一个项目,复制项目地址. 码云地址:https://gitee.com/ 3. ...

  2. JavaSE基础(十一)--Java数组

    Java 数组 数组对于每一门编程语言来说都是重要的数据结构之一,当然不同语言对数组的实现及处理也不尽相同. Java 语言中提供的数组是用来存储固定大小的同类型元素. 数组特点: 其长度是确定的.数 ...

  3. Java反射:Web学习的灵魂

    反射:Web学习的灵魂 我们从最初的 javac -HelloWorld.java,到面向对象部分,我们可以将Java代码在计算机中经历的阶段分为三部分:Scource源代码阶段 -- Class类对 ...

  4. java知识随笔整理-标量函数和表值函数

    以sql server为例: 1.表值函数 用户定义表值函数返回 table 数据类型,表是单个 SELECT 语句的结果集. 示例代码CREATE FUNCTION Test_GetEmployee ...

  5. 转载:同一台电脑教你配置多个Tomcat的环境变量

    装两个tomcat 分别是6.0和7.0 可想运行tomcat6.0 但是实际上却运行tomcat7.0 两个版本都是用解压缩包 其实就是不能运行tomcat6.0 只能运行7.0 两个环境变量都配置 ...

  6. MyBatis使用小结

  7. VS2017的一些调试方法技巧

    一.基本的操作. 1.启动调试. 可以通过VS的调试(Debug)菜单启动调试.点击调试菜单下的“启动调试”或者按F5键启动.如果你已经在代码中加入了断点,那么执行会自动开始. 注:退出调试快捷键sh ...

  8. 怎样设置Cookie

    因为 Cookie 是服务器保存在浏览器中的一小段信息, 因此这个设置应当是服务器发起的, 设置方法是在Response Header中添加: Set-Cookie字段, 值是多个键值对. 如下: / ...

  9. 怎样发出一个HTTP请求

    需要使用 xhr.send(); 参数为请求数据体, 如果没有就传入null, 一般来说, GET请求是不用传参的, POST就视情况而定, 理论上所有GET请求都可以改为POST, 反之则不行. v ...

  10. 8-MySQL DBA笔记-测试基础

    第三部分 测试篇 测试需要掌握的知识面很广泛,本篇的关注点是数据库的性能测试和压力测试,对于其他领域的测试,由于涉猎不多,笔者就不做叙述了.DBA的工作职责之一就是评估软硬件,这往往是一项很耗时的工作 ...