您如何编组sql.NullString以便将输出展平以仅给出go中的值?

时间:2018-08-22 07:00:51

标签: json go marshalling

给出一个go结构

type Company struct {
    ID   int             `json:"id"`              
    Abn  sql.NullString  `json:"abn,string"`
}

被编组成这样的东西

company := &Company{}
company.ID = 68
company.Abn = "SomeABN"
result, err := json.Marshal(company)

结果是

{
    "id": "68",
    "abn": {
        "String": "SomeABN",
        "Valid": true
    }
}

所需结果是

{
    "id": "68",
    "abn": "SomeABN"
}

我已经尝试过明确指出Abn是一个字符串。

Abn  sql.NullString  `json:"abn,string"`

没有改变结果。

如何编组sql.NullString以便将输出展平以仅给出go中的值?

编辑

阅读https://stackoverflow.com/users/8256506/nilsockethttps://stackoverflow.com/users/965900/mkopriva的答案后,我最终得到了类似的结果

package main

import (
    "database/sql"
    "encoding/json"
    "reflect"
    //"github.com/lib/pq"
)

/*
    https://medium.com/aubergine-solutions/how-i-handled-null-possible-values-from-database-rows-in-golang-521fb0ee267
*/

type NullString sql.NullString

func (x *NullString) MarshalJSON() ([]byte, error) {
    if !x.Valid {
        x.Valid = true
        x.String = ""
        //return []byte("null"), nil
    }
    return json.Marshal(x.String)
}

// Scan implements the Scanner interface for NullString
func (ns *NullString) Scan(value interface{}) error {
    var s sql.NullString
    if err := s.Scan(value); err != nil {
        return err
    }

    // if nil then make Valid false
    if reflect.TypeOf(value) == nil {
        *ns = NullString{s.String, false}
    } else {
        *ns = NullString{s.String, true}
    }

    return nil
}

type Company struct {
    ID                     int             `json:"id"`    
    Abn                    NullString      `json:"abn"`         
}

3 个答案:

答案 0 :(得分:2)

这是代码,

package main

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "log"
)

//Company details
type Company struct {
    ID  int        `json:"id"`
    Abn NullString `json:"abn"`
}

//NullString is a wrapper around sql.NullString
type NullString sql.NullString

//MarshalJSON method is called by json.Marshal,
//whenever it is of type NullString
func (x *NullString) MarshalJSON() ([]byte, error) {
    if !x.Valid {
        return []byte("null"), nil
    }
    return json.Marshal(x.String)
}

func main() {
    company := &Company{}
    company.ID = 68
    //create new NullString value
    nStr := sql.NullString{String: "hello", Valid: true}
    //cast it
    company.Abn = NullString(nStr)
    result, err := json.Marshal(company)
    if err != nil {
        log.Println(err)
    }
    fmt.Println(string(result))
}

Here是详细说明它的博客文章。

答案 1 :(得分:1)

您不能,至少不能仅使用sql.NullStringencoding/json

您可以做的是声明一个嵌入sql.NullString的自定义类型,并使该自定义类型实现json.Marshaler接口。

type MyNullString struct {
    sql.NullString
}

func (s MyNullString) MarshalJSON() ([]byte, error) {
    if s.Valid {
        return json.Marshal(s.String)
    }
    return []byte(`null`), nil
}

type Company struct {
    ID   int          `json:"id"`              
    Abn  MyNullString `json:"abn,string"`
}

https://play.golang.org/p/Ak_D6QgIzLb

答案 2 :(得分:1)

该问题表明您希望将数据库结构公开为JSON(可能是REST-ish)API。除非项目寿命短或逻辑层不重要,否则这种方法被视为反模式。内部结构(数据库结构)与外部接口(API)耦合在一起,并可能导致进行更改的成本较高。

我要附上一些读物,因为Google充满了有关如何执行相反操作的教程:

https://lostechies.com/jimmybogard/2016/05/12/entities-arent-resources-resources-arent-representations/

https://thorben-janssen.com/dont-expose-entities-in-api/