Custom JSON Marshalling in Go

고마영·2020년 12월 11일

Go

목록 보기
1/1

Custom JSON Marshalling in Go

Go의 encoding/json package는 struct를 JSON data로 마샬(marshal)하는데 굉장히 쉽게 해준다.

Go에서의 마샬 샘플 코드

package main

import (
	"encoding/json"
	"os"
	"time"
)

type MyUser struct {
	ID       int64     `json:"id"`
	Name     string    `json:"name"`
	LastSeen time.Time `json:"lastSeen"`
}

func main() {
	_ = json.NewEncoder(os.Stdout).Encode(
		&MyUser{1, "Ken", time.Now()},
	)
}

출력

{"id":1,"name":"Ken","lastSeen":"2009-11-10T23:00:00Z"}

만약에 값들 중에서 한 개를 다르게 보여주고 싶다면? 예를 들어 lastSeen의 값을 unix timestamp로 보여주는 것이다.
간단한 해결책으로는 아래 코드와 같이 다른 보조의 struct를 생성하고, MarshalJSON method에 옳바른 포멧 값을 채워 넣는 방법이다.

func (u *MyUser) MarshalJSON() ([]byte, error) {
	return json.Marshal(&struct {
		ID       int64  `json:"id"`
		Name     string `json:"name"`
		LastSeen int64  `json:"lastSeen"`
	}{
		ID:       u.ID,
		Name:     u.Name,
		LastSeen: u.LastSeen.Unix(),
	})
}

잘 작동한다. 그러나 많은 필드에 대해서 작업이 필요할때 굉장히 번거로워진다. 이건 보조 struct안에 원래 struct를 임베딩하면 기존의 모든 필드를 잘 상속 받아 변경할 필요 없이 깔끔하게 작동할 것이다.

func (u *MyUser) MarshalJSON() ([]byte, error) {
	return json.Marshal(&struct {
		LastSeen int64 `json:"lastSeen"`
		*MyUser
	}{
		LastSeen: u.LastSeen.Unix(),
		MyUser:   u,
	})
}

이때 보조 struct원래 structMarshalJSON method를 상송을 받게 되어, MarshalJSON를 무한호출하는 것에 빠지고 말것이다. 이때 원래 struct를 별칭(alias)으로 지정해줌으로써 해결이 가능하다. 이 alias는 메소드를 제외한 동일한 모든 필드를 가지게 된다.

func (u *MyUser) MarshalJSON() ([]byte, error) {
	type Alias MyUser
	return json.Marshal(&struct {
		LastSeen int64 `json:"lastSeen"`
		*Alias
	}{
		LastSeen: u.LastSeen.Unix(),
		Alias:    (*Alias)(u),
	})
}

같은 방식으로 UnmarshalJSON의 구현도 가능하다.

func (u *MyUser) UnmarshalJSON(data []byte) error {
	type Alias MyUser
	aux := &struct {
		LastSeen int64 `json:"lastSeen"`
		*Alias
	}{
		Alias: (*Alias)(u),
	}
	if err := json.Unmarshal(data, &aux); err != nil {
		return err
	}
	u.LastSeen = time.Unix(aux.LastSeen, 0)
	return nil
}

출처

http://choly.ca/post/go-json-marshalling/

profile
항상 겸손하게

0개의 댓글