冷寒月 發表於 2020-12-7 19:45:00

Go语言的omitempty

<h3 id="使用">使用</h3>
<p>熟悉 Golang 的朋友对于 tag、json 和 struct 都不陌生。</p>
<pre><code class="language-go">type Address struct {
        City   string `json:"city"`
        Street string `json:"street"`
        ZipCode string `json:"zip_code"`
}

func TestMarshal(t *testing.T) {
        data := `{
      "city": "Beijing",
      "street": "a"
    }`
        addr := &amp;Address{}
        json.Unmarshal([]byte(data), addr)
        fmt.Printf("%#v\n", addr)

        addressBytes, _ := json.MarshalIndent(addr, "", "    ")
        fmt.Printf("%s\n", string(addressBytes))
}
/*
&amp;omitempty.Address{City:"Beijing", Street:"a", ZipCode:""}
{
    "city": "Beijing",
    "street": "a",
    "zip_code": ""
}
*/
</code></pre>
<p>我们可以看到,多了一行 <code>"zip_code": "",</code> ,而这则信息在原本的 json 数据中是没有的,但我们更希望的是,在一个地址有 zip_code 号码的时候输出,不存在 zip_code 的时候就不输出,幸运的是,我们可以在 Golang 的结构体定义中添加 <code>omitempty</code> 关键字,来表示这条信息如果没有提供,在序列化成 json 的时候就不要包含其默认值。稍作修改,地址结构体就变成了</p>
<pre><code class="language-go">type Address struct {
        City    string `json:"city"`
        Streetstring `json:"street"`
        ZipCode string `json:"zip_code,omitempty"`
}

func TestMarshal(t *testing.T) {
        data := `{
      "city": "Beijing",
      "street": "a"
    }`
        addr := &amp;Address{}
        json.Unmarshal([]byte(data), addr)
        fmt.Printf("%#v\n", addr)

        addressBytes, _ := json.MarshalIndent(addr, "", "    ")
        fmt.Printf("%s\n", string(addressBytes))
}
/*
&amp;omitempty.Address{City:"Beijing", Street:"a", ZipCode:""}
{
    "city": "Beijing",
    "street": "a"
}
*/
</code></pre>
<p>成功解决。</p>
<hr>
<h3 id="陷阱">陷阱</h3>
<p>带来方便的同时,使用 <code>omitempty</code> 也有些小陷阱。</p>
<h4 id="陷阱一关键字无法忽略掉嵌套结构体">陷阱一:关键字无法忽略掉嵌套结构体</h4>
<p>还是拿地址类型说事,这回我们想要往地址结构体中加一个新的结构体来表示经纬度,如果没有或缺乏相关的数据,可以忽略。新的 struct 定义如下所示</p>
<pre><code class="language-go">type Address struct {
        City       string   `json:"city"`
        Street   string   `json:"street"`
        ZipCode    string   `json:"zip_code,omitempty"`
        Coordinate coordinate `json:"coordinate,omitempty"`
}

type coordinate struct {
        Lat float64 `json:"latitude"`
        Lng float64 `json:"longitude"`
}

func TestMarshal(t *testing.T) {
        data := `{
      "city": "Beijing",
      "street": "a"
    }`
        addr := &amp;Address{}
        json.Unmarshal([]byte(data), addr)

        addressBytes, _ := json.MarshalIndent(addr, "", "    ")
        fmt.Printf("%s\n", string(addressBytes))
}
/*
{
    "city": "Beijing",
    "street": "a",
    "coordinate": {
      "latitude": 0,
      "longitude": 0
    }
}
*/
</code></pre>
<p>读入原来的地址数据,处理后序列化输出,我们就会发现<strong>即使对新的结构体字段加上了 <code>omitempty</code> 关键字,输出的 json 还是带上了一个空的坐标信息。</strong></p>
<p>为了达到我们想要的效果,可以把<strong>坐标结构体定义为指针类型</strong>,这样 Golang 就能知道一个指针的“空值”是多少了,否则面对一个我们自定义的结构, Golang 是猜不出我们想要的空值的。于是有了如下的结构体定义:</p>
<pre><code class="language-go">type Address struct {
        City       string      `json:"city"`
        Street   string      `json:"street"`
        ZipCode    string      `json:"zip_code,omitempty"`
        Coordinate *coordinate `json:"coordinate,omitempty"`
}

type coordinate struct {
        Lat float64 `json:"latitude"`
        Lng float64 `json:"longitude"`
}
// ... 同上 忽略
/*
{
    "city": "Beijing",
    "street": "a"
}
*/
</code></pre>
<h4 id="2-陷阱二想要传入零值">2. 陷阱二:想要传入零值</h4>
<p>对于用 <code>omitempty</code> 定义的 字段 ,<strong>如果给它赋的值恰好等于默认空值的话</strong>,在转为 json 之后也不会输出这个 字段 。比如说上面定义的经纬度坐标结构体,如果我们将经纬度两个 字段 都加上 <code>omitempty</code></p>
<pre><code class="language-go">type coordinate struct {
        Lat float64 `json:"latitude,omitempty"`
        Lng float64 `json:"longitude,omitempty"`
}

func TestMarshal(t *testing.T) {
        data := `{
      "latitude": 1.0,
      "longitude": 0.0
    }`
        c := &amp;coordinate{}
        json.Unmarshal([]byte(data), c)
        fmt.Printf("%#v\n", c)

        addressBytes, _ := json.MarshalIndent(c, "", "    ")
        fmt.Printf("%s\n", string(addressBytes))
}
/*
&amp;omitempty.coordinate{Lat:1, Lng:0}
{
    "latitude": 1
}
*/
</code></pre>
<p>这个坐标的<code>longitude</code>消失不见了!</p>
<p>但我们的设想是,如果一个地点没有经纬度信息,则悬空,这没有问题,但对于“原点坐标”,我们在确切知道它的经纬度的情况下,(0.0, 0.0)仍然被忽略了。正确的写法也是将结构体内的定义改为指针</p>
<pre><code class="language-go">type coordinate struct {
    Lat *float64 `json:"latitude,omitempty"`
    Lng *float64 `json:"longitude,omitempty"`
}
/*
&amp;omitempty.coordinate{Lat:(*float64)(0xc0000a6288), Lng:(*float64)(0xc0000a6298)}
{
    "latitude": 1,
    "longitude": 0
}
*/
</code></pre>
<p>这样空值就从 <code>float64</code> 的 0.0 变为了指针类型的 <code>nil</code> ,我们就能看到正确的经纬度输出。</p>
<ul>
<li>Golang 的 “omitempty” 关键字略解</li>
</ul>


</div>
<div id="MySignature" role="contentinfo">
    © 2017-2020 版权属于 QXQZX &amp;<br><br>
来源:https://www.cnblogs.com/devhg/p/14099212.html
頁: [1]
查看完整版本: Go语言的omitempty