Tyndyll
Tyndyll

Reputation: 344

MarshalJSON a String Declared Type

I have created a new declared type and added a method to marshal the value into JSON

type TextOutput string

func (t *TextOutput) MarshalJSON() ([]byte, error) {
    return []byte(fmt.Sprintf(`{"data": "%s"}`, t)), nil
}

When I try to marshal an instance of the type I get the raw value returned. What am I missing?

var t TextOutput
t = `Test test`
output, err := json.Marshal(t)
if err != nil {
    fmt.Println(err)
} else {
    fmt.Println(string(output))
}
// prints Test Test. Expected {"data": "Test test"}

Upvotes: 1

Views: 770

Answers (2)

derickson82
derickson82

Reputation: 496

The root of the problem stems from how interfaces in Go are implicitly satisfied.
In this particular case, the json.Marshal method uses type assertion at runtime to see if the given value implements json.Marshaler. Effective Go mentions this very case.

You could have satisfied the json.Marshaler for the *TextOutput type using a pointer-receiver like so:

func (t *TextOutput) MarshalJSON() ([]byte, error) {
    return []byte(fmt.Sprintf(`{"data":"%s"}`, *t)), nil
}

And for this to work properly, pass the reference to the json.Marshal function:

var t TextOutput
t = `Test test`
output, err := json.Marshal(&t) 

However, implementing it using a value-receiver ensures that both TextOutput and *TextOutput types implement json.Marshaler

func (t TextOutput) MarshalJSON() ([]byte, error) {
    return []byte(fmt.Sprintf(`{"data": "%s"}`, t)), nil
}

Upvotes: 1

jeevatkm
jeevatkm

Reputation: 4791

You have to define the MarshalJSON interface as a non-pointer.

func (t TextOutput) MarshalJSON() ([]byte, error) {
    return []byte(fmt.Sprintf(`{"data": "%s"}`, t)), nil
}

Play Link: https://play.golang.org/p/lLK6zsAkOi

Output:

{"data":"Test test"}

Upvotes: 2

Related Questions