fmt格式说明符仅打印具有非零值的字段

Consider this code:

package main

import (
    "fmt"
)

type myStruct struct {
    a string
    b int
    c bool
}

func main() {

    s := myStruct{
        c: true,
    }
    fmt.Printf("%v", s)
    // print { 0 true}
    fmt.Printf("%+v", s)
    // print {a: b:0 c:true}
}

Is there a fmt format specifier to print only fields with non-zero value?

For example, with the code above, how can I print only

{c:true} 

because a == "" and b == 0?

There is no built-in format verb that causes zero values to be omitted.

Here are some options.

fmt.Stringer

You can hard-code the string format for your type by implementing fmt.Stringer:

package main

import (
    "fmt"
    "strings"
)

type myStruct struct {
    a string
    b int
    c bool
}

func (s myStruct) String() string {
    var fields []string

    if s.a != "" {
        fields = append(fields, fmt.Sprintf("a:%q", s.a))
    }

    if s.b != 0 {
        fields = append(fields, fmt.Sprintf("b:%d", s.b))
    }

    if s.c {
        fields = append(fields, fmt.Sprintf("c:%t", s.c))
    }

    return fmt.Sprintf("{%s}", strings.Join(fields, ","))
}

func main() {
    s := myStruct{a: "foo"}

    fmt.Println(s)
}

Output:

{a:"foo"}

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


Reflection

You can use reflection to build something that will work with any struct, but it is perhaps more hassle than it is worth. Example omitted.


JSON

Another alternative is to marshal it to JSON, which handles the reflection part and has support for omitting zero values. Example:

package main

import (
    "encoding/json"
    "log"
    "os"
)

type myStruct struct {
    A string `json:",omitempty"`
    B int    `json:",omitempty"`
    C bool   `json:",omitempty"`
}

func main() {
    s := myStruct{A: "foo"}

    if err := json.NewEncoder(os.Stdout).Encode(s); err != nil {
        log.Fatal(err)
    }
}

Output:

{"A":"foo"}

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


JSON with unexported fields

If you prefer to keep the original struct as-is; you can define a custom marshaller with an anonymous struct. Note however that the struct format is then duplicated in the MarshalJSON method which adds a bit of complexity:

package main

import (
    "encoding/json"
    "log"
    "os"
)

type myStruct struct {
    a string
    b int
    c bool
}

func (s myStruct) MarshalJSON() ([]byte, error) {
    return json.Marshal(
        struct {
            A string `json:"a,omitempty"`
            B int    `json:"b,omitempty"`
            C bool   `json:"c,omitempty"`
        }{
            A: s.a,
            B: s.b,
            C: s.c,
        },
    )
}

func main() {
    s := myStruct{a: "foo"}

    if err := json.NewEncoder(os.Stdout).Encode(s); err != nil {
        log.Fatal(err)
    }
}

Output:

{"a":"foo"}

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


JSON with unexported fields + fmt.Stringer

If you want, you can again implement fmt.Stringer, which fmt.Printf and friends will pick up:

package main

import (
    "encoding/json"
    "fmt"
)

type myStruct struct {
    a string
    b int
    c bool
}

func (s myStruct) MarshalJSON() ([]byte, error) {
    return json.Marshal(
        struct {
            A string `json:"a,omitempty"`
            B int    `json:"b,omitempty"`
            C bool   `json:"c,omitempty"`
        }{
            A: s.a,
            B: s.b,
            C: s.c,
        },
    )
}

func (s myStruct) String() string {
    j, err := json.Marshal(s)
    if err != nil {
        return ""
    }
    return string(j)
}

func main() {
    s := myStruct{a: "foo"}

    fmt.Println(s)
}

Output:

{"a":"foo"}

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