简体   繁体   中英

how to print original go struct in String method

I have a struct defined as follows

type WallpaperType int

// ...

type WallpaperInfo struct {
    WallpaperType WallpaperType `json:"type"`
    HelpMsg       string        `json:"help"`
    Path          string        `json:"path"`
    ImageColor    color.RGBA    `json:"imageColor"`
}

Printing such a struct using

winfo := &WallpaperInfo{...}
log.Println(winfo)
log.Printf("%+v\n", winfo)

Gives something like this

&{Slideshow Wallpaper is a slideshow D:\Images\Wallpapers\autumn-autumn-leaves-blur-close-up-589840.jpg {219 77 66 167}}

&{WallpaperType:Slideshow HelpMsg:Wallpaper is a slideshow Path:D:\Images\Wallpapers\autumn-autumn-leaves-blur-close-up-589840.jpg ImageColor:{R:219 G:77 B:66 A:167}}

I'd like to print it as a JSON so I've implemented the String method for the struct as follows

func (w WallpaperInfo) String() string {
    bytes, err := json.Marshal(w)
    if err != nil {
        return "" // ???
        // this will call the method recursively
        // return fmt.Sprintf("%+v\n", w)
    }
    return string(bytes)
}

In case there's an error in the String() method,

I'd like to print the original &{WallpaperType:Slideshow HelpMsg:Wallpaper is a slideshow Path:D:\\Images\\Wallpapers\\autumn-autumn-leaves-blur-close-up-589840.jpg ImageColor:{R:219 G:77 B:66 A:167}}

I've tried returning fmt.Sprintf("%+v\\n", w) but it does recursive call to the same function.

Any way I can return the original struct format? Because if json fails I don't know a way to print it differently.

Create a new type with WallpaperInfo as its underlying type , and convert the value before passing it to fmt.Sprintf() . Creating a new type will strip it from all its methods, including the String() method:

func (w WallpaperInfo) String() string {
    bytes, err := json.Marshal(w)
    if err != nil {
        type wi WallpaperInfo
        return fmt.Sprintf("%+v\n", wi(w))
    }
    return string(bytes)
}

Although your type can never return JSON marshaling error, but in the general case this is how you can achieve what you want.

Here's an example that fails JSON marshaling on purpose, and will fall back to original struct string representation:

type fail int

func (fail) MarshalJSON() ([]byte, error) {
    return nil, errors.New("on-purpose error")
}

type MyStruct struct {
    F fail
}

func (m MyStruct) String() string {
    bytes, err := json.Marshal(m)
    if err != nil {
        log.Printf("JSON error: %v", err)
        type ms MyStruct
        return fmt.Sprintf("%+v\n", ms(m))
    }
    return string(bytes)
}

Testing it:

s := MyStruct{}

log.Println(s)
log.Printf("%+v\n", s)

Which will output (try it on the Go Playground ):

2009/11/10 23:00:00 JSON error: json: error calling MarshalJSON for type main.fail: on-purpose error
2009/11/10 23:00:00 {F:0}

2009/11/10 23:00:00 JSON error: json: error calling MarshalJSON for type main.fail: on-purpose error
2009/11/10 23:00:00 {F:0}

See related question:

Call json.Unmarshal inside UnmarshalJSON function without causing stack overflow

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM