-
Golang: Nested structs to JSON (중첩 struct Json만들기)컴퓨터/Go language 2021. 1. 3. 18:45728x90반응형
Struct안에 anonymous 구조체나 구조체를 계속 중첩해서 만들 수 있다.
하지만 이 방식은 점점 익명 구조체가 많아질수록 코딩이 길어지고 힘들다.
그래서 익명 구조체를 쓰지 않고 하나하나 모델을 다 만들어서 사용하는 게 일반적이다.
1. 중첩 익명 구조체
type Nested struct { Hello struct { Inside string `json:"inside"` } `json:"hello"` Bye string `json:"bye"` } // 메인 함수 func main() { var data Nested data = Nested{ Hello: struct { Inside string `json:"inside"` }{Inside: "hello"}, Bye: "world", } b, _ := json.MarshalIndent(data, "", " ") fmt.Println(string(b)) }
1. JSON Marshal 결과
{ "hello": { "inside": "hello" }, "bye": "world" }
omitempty tag
기본 json 패키지는
omitempty를 적용하면, string, int 빈 값이면 출력을 알아서 제외하지만
구조체가 비었을 때를 처리하지 않는다. (따로 marshal 함수 만들어야함)
(또는 *포인터를 줘서 nil로 처리해서 스킵할 수 있음)
clarketm의 json패키지를 이용하면 구조체를 사용 안 할 때도 json결과에서 제외해준다.
go get -u github.com/clarketm/json
맨 위 중첩된 익명 구조체를 따로 하나 만들고, omitempty를 Bye에 적용한 결과이다.
2. struct omitempty
package main import ( "fmt" "github.com/clarketm/json" ) type Nested struct { Hello Hello `json:"hello,omitempty"` Bye string `json:"bye,omitempty"` } type Hello struct { Inside string `json:"inside"` } func main() { var data Nested data = Nested{ // Hello: Hello{Inside: "hello"}, Bye: "bye", } b, _ := json.MarshalIndent(data, "", " ") fmt.Println(string(b)) }
2. JSON Marshal 결과
{ "bye": "bye" }
[]struct 구조
때로는 json 형식이 괴상해서, 아래처럼 배열 안 구조체를 이용해야 하는 경우가 있다.
json["template"]["outputs"] = [ tag 없는 구조체 { "listCard" tag 구조체 {...
예제) 카카오 챗봇 ListCard JSON
{ "version": "2.0", "template": { "outputs": [ { "listCard": { "header": { "title": "title" }, "items": [ { "title": "card title", "description": "card desc", "imageUrl": "this can be null", // Optional "link": { "web": "web url" } // Optional } ], "buttons": [ { "label": "sharetext", "action": "share" }, { "label": "labeltext", "action": "webLink", "webLinkUrl": "weburl" // Optional } ] } } ], "quickReplies": [ { "messageText": "msg1", "action": "message", "label": "label1" }, { "messageText": "msg2", "action": "message", "label": "label2" } ] } }
위와 같은 경우는 차근차근 하나씩 다 만들거나
[]byte로 json text를 unmarshal 해서 사용해도 되는데,
이 글에선 하나씩 모델을 FastAPI BaseModel처럼 만들어 볼 것이다.
메인
{ "template": ..., "version": ... }
// KakaoListCard is main type KakaoListCard struct { Template Template `json:"template"` Version string `json:"version"` // 2.0 }
json["template"]
type Template struct { Outputs []struct { ListCard ListCardJSON `json:"listCard"` } `json:"outputs"` QuickReplies []QuickReply `json:"quickReplies,omitempty"` }
Outputs은 array of struct 형태고, 그 안은 "listCard"라는 key가 있다.
QuickReply는 omitempty로 있어도 되고 없어도 된다.
Outputs을 []ListCard로 하면 될 것 같지만 이렇게 하면, "listCard"라는 key를 배열 안에 줄 수가 없었다.
{ "template": { "outputs": [ { // "listCard": { 로 되어야 함.
json["template"]["outputs", "quickReplies"]
// json["template"]["outputs"] type ListCardJSON struct { Buttons []Button `json:"buttons"` Header Header `json:"header,omitempty"` Items []Item `json:"items"` } // json["template"]["quickReplies"] type QuickReply struct { Action string `json:"action"` Label string `json:"label"` MessageText string `json:"messageText"` }
json["template"]["outputs"]["buttons"]
type Button struct { Label string `json:"label"` Action string `json:"action"` }
이후 결과 struct
// KakaoListCard is main type KakaoListCard struct { Template Template `json:"template"` Version string `json:"version"` // 2.0 } type Template struct { Outputs []struct { ListCard ListCardJSON `json:"listCard"` } `json:"outputs"` QuickReplies []QuickReply `json:"quickReplies,omitempty"` } type QuickReply struct { Action string `json:"action"` Label string `json:"label"` MessageText string `json:"messageText"` } type ListCardJSON struct { Buttons []Button `json:"buttons"` Header Header `json:"header,omitempty"` Items []Item `json:"items"` } type Button struct { Label string `json:"label"` Action string `json:"action"` } type Item struct { Description string `json:"description"` ImageUrl string `json:"imageUrl,omitempty"` Link Link `json:"link,omitempty"` Title string `json:"title"` } type Link struct { Web string `json:"web"` } type Header struct { Title string `json:"title"` }
카드, quickReplies 만들고 JSON Marshal
KakaoListCard 형식 초기화
data := KakaoListCard{Version: "2.0"}
카드 추가
// Card Buttons buttons := []Button{{Label: "hey1", Action: "share"}, {Label: "hey2", Action: "share"}} // Card Contents items := []Item{ {Description: "desc1", ImageUrl: "img", Link: Link{Web: "web"}, Title: "title1"}, {Description: "desc2", Title: "title2"}, } // "outputs": { "listCard" { ... listCard := ListCardJSON{ Buttons: buttons, Header: Header{Title: "hi"}, Items: items, }
카드 템플릿에 추가
익명 구조체를 썼기 때문에, 값을 넣을 때 다시 구조체를 똑같이 만들고 값을 뒤에 만들어 주어야 함.
// Add outputs to template var temp []struct { ListCard ListCardJSON `json:"listCard"` } lc := struct { ListCard ListCardJSON `json:"listCard"` }{ListCard: listCard} temp = append(temp, lc) data.Template.Outputs = temp // To make a tag [{"listCard"}]
quickReplies 만들기 (옵션)
// Make QuickReplies quickreplies := []QuickReply{{ MessageText: "어제 보여줘", Action: "message", Label: "어제", }} data.Template.QuickReplies = quickreplies
JSON Marshal
import ( "fmt" "github.com/clarketm/json" ) b, _ := json.MarshalIndent(data, "", " ") fmt.Println(string(b))
JSON 출력 결과
{ "template": { "outputs": [ { "listCard": { "buttons": [ { "label": "hey1", "action": "share" }, { "label": "hey2", "action": "share" } ], "header": { "title": "hi" }, "items": [ { "description": "desc1", "imageUrl": "img", "link": { "web": "web" }, "title": "title1" }, { "description": "desc2", "title": "title2" } ] } } ], "quickReplies": [ { "action": "message", "label": "어제", "messageText": "어제 보여줘" } ] }, "version": "2.0" }
조금 더 쉽게
type Thumbnail struct { ImageUrl string `json:"imageUrl"` } type Profile struct { ImageUrl string `json:"imageUrl"` Nickname string `json:"nickname"` } type Social struct { Like int `json:"like"` Comment int `json:"comment"` Share int `json:"share"` } type Button struct { Action string `json:"action"` Label string `json:"label"` MessageText string `json:"messageText,omitempty"` WebLinkUrl string `json:"webLinkUrl,omitempty"` } type BasicCard struct { Title string `json:"title"` Description string `json:"description"` Thumbnail Thumbnail `json:"thumbnail"` Profile Profile `json:"profile"` Social Social `json:"social"` Buttons []Button `json:"buttons"` } type ListCard struct { Header struct { Title string `json:"title"` } `json:"header"` Items []struct { Title string `json:"title"` Description string `json:"description"` ImageUrl string `json:"imageUrl,omitempty"` Link struct { Web string `json:"web"` } `json:"link,omitempty"` } `json:"items"` Buttons []Button `json:"buttons"` } type Output struct { BasicCard *BasicCard `json:"basicCard,omitempty"` ListCard *ListCard `json:"listCard,omitempty"` } type Template struct { Outputs []Output `json:"outputs"` } type JSONStruct struct { Version string `json:"version"` Template Template `json:"template"` } buttons := []Button{ { Action: "message", Label: "열어보기", MessageText: "짜잔! 우리가 찾던 보물입니다", }, { Action: "webLink", Label: "구경하기", WebLinkUrl: "https://e.kakao.com/t/hello-ryan", }, }
Python FastAPI를 이용해서 할 때
dictionary key로 쉽게 접근할 수 있어서 편하다.
Go에서는 map[string]interface{} 형식을 쓰면 key접근을 사용할 수 있다.
@application.post("/date") def postSome(content: Dict): print(content["action"]["params"]["date"]) return JSONResponse(content={})
Go ListCard JSON Marshal 소스 보기
728x90'컴퓨터 > Go language' 카테고리의 다른 글
Golang: soup를 이용한 네이버 날씨 정보 가져오기 (0) 2021.02.14 Golang: JSON <, >, & HTML 기호 escape 하기 (0) 2021.02.09 Golang: gin으로 카카오 챗봇 서버 만들기 (2) 2021.01.01