Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to implement String() method with optional fields in Go?

I am implementing a String() method on a type I have. This type’s fields provide optional configuration options, and a user may choose to add any combination of those fields without necessarily needing all of them. The end result needs to be formatted as a JSON string (dictated by external service I’m consuming, not my choice).

Edit: a bit of additional context. The reason I need a String() method is because when I actually provide a value of this type to the external service, it will be formatted as a URL encoded string.

My approach was something along the lines of:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel


type AnotherStruct struct {
    FieldA []string
    FieldB []string
}

type Spec struct {
    FieldA []string
    FieldB int
    FieldC AnotherStruct
}

func (s Spec) String() string {
    args := make([]any, 0)
    f := "{"
    if s.FieldA != nil {
        f += `"fieldA": %v, `
        args = append(args, s.FieldA)
    }
    if s.FieldB != 0 {
        f += `"fieldB": %d, `
        args = append(args, s.FieldB)
    }
    if !reflect.DeepEqual(s.FieldC, AnotherStruct{}) {
        f += `"fieldC": %v, `
        args = append(args, s.FieldC)
    }
    f += "}"

    return fmt.Sprintf(f, args...)
}

But this seems clunky. Is there a clean way to implement such a method in Go?

>Solution :

There’s no reason to create a String method in your case. Everything* you’re aiming to do is already handled by the standard library encoding/json package.

The idiomatic way to do what you’re after is with JSON struct tags:

type Spec struct {
    FieldA []string      `json:"fieldA,omitempty"`
    FieldB int           `json:"fieldB,omitempty"`
    FieldC *AnotherStruct `json:"fieldC,omitempty"`
}

Then just call json.Marshal() on your value:

s := Spec{
    FieldA: []string{"one", "two"},
    // FieldB & FieldC omitted
}
output, err := json.Marshal(s)
if err != nil {
    panic(err)
}
fmt.Println(string(output))

Prints:

{"fieldA":["one","two"]}

*The only change I had to make to conform to the standard library expectations was to make FieldC a pointer to AnotherStruct:

    FieldC *AnotherStruct `json:"fieldC,omitempty"`

instead of

    FieldC AnotherStruct `json:"fieldC,omitempty"`
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading