Skip to content

Instantly share code, notes, and snippets.

@kenkeiter
Created October 5, 2014 16:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kenkeiter/2fbe5b0f87a00a821086 to your computer and use it in GitHub Desktop.
Save kenkeiter/2fbe5b0f87a00a821086 to your computer and use it in GitHub Desktop.
Quick thoughts on separation of representation from implementation in Go serialization.

If your application demands marshaling to JSON or another serialiazation format using Go, it can be tempting to design your structures to be cleanly serializable – often to the detriment of the implementation.

You might find yourself exporting variables you shouldn't be exporting, or creating elaborate ways to prevent mutability of attributes you care about.

If you find yourself doing this, stop, and consider the separation of concerns. JSON is a representation of your structure, and should not be directly tied to its underlying implementation, unless it's convenient. Although it's a bit more expensive, consider creating custom marshaling functions and generating one-off structs with the exact fields you need to decouple the implementation from representation.

In the following example, you don't want to export the Value field from the Counter struct, because it exposes your value to potentially unsafe operations (two incrementations at the same time, for example):

type Counter struct {
	Name string `json:"name"`
	Value int64 `json:"value"`
}

func (c *Counter) Incr(delta int64) {
	atomic.AddInt64(&c.Value, delta)
}

Instead, create custom marshal or unmarshal functions to generate a representation of your counter, and keep the design of your interface safe. Yes, it's more code, but it no longer influences your internal API or implementation.

type Counter struct {
	Name string
	value int64
}

func (c *Counter) Incr(delta int64) {
	atomic.AddInt64(&c.value, delta)
}

func (c *Counter) MarshalJSON() ([]byte, error) {
	repr := struct {
		Name string `json:"name"`
		Value int64 `json:"value"`
	}{
		c.Name,
		atomic.LoadInt64(&c.value),
	}
	data, err := json.Marshal(repr)
	return data, err
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment