go-ozzo/ozzo-routing

Routing.Context.Write() not marshaling JSON as expected

Closed this issue · 2 comments

I have a struct Person with custom JSON MarshalJSON and UnmarshalJSON methods. When I use the the Write() in a routing handler, it skips these custom methods and use the default JSON marshal/unmarshal.

func GetPerson (c *routing.Context) error {
    // ...
    return c.Write(p) // print JSON with default JSON method, expect custom marshal to be used
}

However, if I wrap the struct in another struct or slice, e.g. :

type User struct {
    UserID  int    `json:"userID"`
    Details Person `json:"details"`
}

Or

[]Person{}

and c.Write(...) these values, the custom marshal methods is used.

E.g.

func GetPerson (c *routing.Context) error {
    u := User{}
    // ... more code
    return c.Write(u) // the custom marshal methods in Person is used (e.g. Birthday in Person have the correct time format)
}

func GetPeople (c *routing.Context) error {
    people := []Person{}
    // ... more code
    return c.Write(people) // the custom marshal methods is being used too (e.g. time format is converted as expected)
}

Is it a bug?* I guess it is something related to http Writter*... Hmmm... may please have a look? :)

Note: the Person struct is included below

type Person struct {
	Name     string    `json:"name"`
	Birthday time.Time `json:"birthday"`
	Hobbies  []Sharing `json:"hobbies"`
}

// MarshalJSON marshal struct.
// - Convert empty slice to "[]" instead of "null"
// - Custom manipulation to certain fields
// - Output time.Time in specific time format used by the app
func (m *Person) MarshalJSON() ([]byte, error) {
	type Alias Person
	if m.Hobbies == nil {
		m.Hobbies = make([]Sharing, 0) //convert to empty array if slice is nil
	}

	return json.Marshal(&struct {
		Name     string `json:"name"`
		Birthday string `json:"birthday"`
		*Alias
	}{
		Name:     "Oh, you need to guess it!",  //custom manipulation
		Birthday: app.TimeToString(m.Birthday), //change time format
		Alias:    (*Alias)(m),
	})
}

// UnmarshalJSON unmarshal JSON to struct data
func (m *Person) UnmarshalJSON(data []byte) error {
	type Alias Person
	aux := &struct {
		Birthday string `json:"birthday"`
		*Alias
	}{
		Alias: (*Alias)(m),
	}
	if err := json.Unmarshal(data, &aux); err != nil {
		return err
	}

	t, err = app.StringToTime(aux.Birthday)
	if err != nil {
		return err
	}
	m.Birthday = t

	return nil
}

Declare your MarshalJSON as this: func (m Person) MarshalJSON() ([]byte, error)
(the method is bound to the struct, not struct pointer)

OK, it works... many thanks... ^_^