ABOUT ME

-

Total
-
  • Golang: Nested structs to JSON (중첩 struct Json만들기)
    컴퓨터/Go language 2021. 1. 3. 18:45
    728x90
    반응형

     

    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 소스 보기

     

    Alfex4936/Golang-Studies

    Learning Go (Go언어 공부 모음). Contribute to Alfex4936/Golang-Studies development by creating an account on GitHub.

    github.com

     

    728x90

    댓글