ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Golang] JSON 사용하기
    언어/Golang 2017. 2. 16. 20:44

    Go에서 JSON을 사용하려면 encoding/json패키지를 import해야 합니다. 아래는 해당 패키지에서 제공하는 JSON 함수입니다.

    • func Marshal(v interface{}) ([]byte, error): Go 언어 자료형을 JSON 텍스트로 변환
    • func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error): Go 언어 자료형을 JSON 텍스트로 변환하고 사람이 보기 편하도록 들여쓰기를 해줌
    • func Unmarshal(data []byte, v interface{}) error: JSON 텍스트를 Go 언어 자료형으로 변환

    key-value 구조 JSON

    JSON 직렬화

    아래는 맵 형태의 데이터를 JSON 형태로 변환하는 방법입니다.

    package main
    
    import (
    	"encoding/json"
    	"fmt"
    )
    
    func main() {
    	data := make(map[string]interface{}) // 문자열을 키로하고 모든 자료형을 저장할 수 있는 맵 생성
    
    	data["name"] = "kim"
    	data["age"] = 25
    
    	doc, _ := json.Marshal(data) // 맵을 JSON 문서로 변환
    
    	fmt.Println(string(doc)) // {"age":25,"name":"kim"}: 문자열로 변환하여 출력
    }
     
    # 결과
    {"age":10,"name":"maria"}

    문자열을 키로하고 모든 자료형을 값으로 저장할 수 있는 맵을 할당합니다. 그리고 적절히 데이터를 대입한 뒤 json.Marshal 함수를 사용하여 JSON 문서로 변환합니다. json.Marshal 함수에서 리턴된 값은 []byte 형식이므로 출력할 때는 string 형식으로 변환해줍니다. JSON 문서로 변환했을 때 한 줄로 붙어서 나오면 사람이 읽기 힘듭니다. 따라서 다음과 같이 json.MarshalIndent 함수를 사용하면 사람이 쉽게 읽을 수 있도록 변환할 수 있습니다.


    doc, _ := json.MarshalIndent(data, "", "  ")
     
    # 결과
    {
      "age": 10,
      "name": "maria"
    }

    첫 번째 매개변수는 JSON 문서로 만들 데이터입니다. 그리고 두 번째 매개변수는 JSON 문서의 첫 칸에 표시할 문자열(Prefix)인데 보통 "" (빈 문자)를 지정합니다. 마지막으로 세 번째 매개변수는 들여쓰기할 문자입니다. 공백 문자나 탭 문자를 사용할 수 있습니다. Prefix는 표시하지 않고, 들여쓰기는 2칸으로 지정하면 다음과 같이 JSON 문서로 변환됩니다.

    JSON 역직렬화

    아래는 JSON형태를 Go에서 받아 읽는 방법입니다.

    package main
    
    import (
    	"encoding/json"
    	"fmt"
    )
    
    func main() {
    	doc := `
    	{
    		"name": "kim",
    		"age": 25
    	}
    	`
    
    	var data map[string]interface{} // JSON 문서의 데이터를 저장할 공간을 맵으로 선언
    	json.Unmarshal([]byte(doc), &data) // doc를 바이트 슬라이스로 변환하여 넣고, data의 포인터를 넣어줌
    	fmt.Println(data["name"], data["age"]) // kim 25: 맵에 키를 지정하여 값을 가져옴
    }

    구조체를 활용한 복잡한 JSON 표현

    위 방법은 아주 간단한 1단계인 key-value 구조였습니다. 이번엔 구조체를 활용해 더 복잡한 구조를 표현하겠습니다.

    JSON 역직렬화

    package main
    
    import (
    	"encoding/json"
    	"fmt"
    )
    
    type Author struct {
    	Name  string
    	Email string
    }
    
    type Comment struct {
    	Id      uint64
    	Author  Author // Author 구조체
    	Content string
    }
    
    type Article struct {
    	Id         uint64
    	Title      string
    	Author     Author    // Author 구조체
    	Content    string
    	Recommends []string  // 문자열 배열
    	Comments   []Comment // Comment 구조체 배열
    }
    
    func main() {
    	doc := `
    	[{
    		"Id": 1,
    		"Title": "Hello, world!",
    		"Author": {
    			"Name": "Maria",
    			"Email": "maria@example.com"
    		},
    		"Content": "Hello~",
    		"Recommends": [
    			"John",
    			"Andrew"
    		],
    		"Comments": [{
    			"id": 1,
    			"Author": {
    				"Name": "Andrew",
    				"Email": "andrew@hello.com"
    			},
    			"Content": "Hello Maria"
    		}]
    	}]
    	`
    
    	var data []Article // JSON 문서의 데이터를 저장할 구조체 슬라이스 선언
    
    	json.Unmarshal([]byte(doc), &data) // doc의 내용을 변환하여 data에 저장
    
    	fmt.Println(data) // [{1 Hello, world! {Maria maria@exa... (생략)
    }

    JSON 직렬화

    데이터를 JSON 형태로 변환해보겠습니다.

    package main
    
    import (
    	"encoding/json"
    	"fmt"
    )
    
    type Author struct {
    	Name  string
    	Email string
    }
    
    type Comment struct {
    	Id      uint64
    	Author  Author // Author 구조체
    	Content string
    }
    
    type Article struct {
    	Id         uint64
    	Title      string
    	Author     Author    // Author 구조체
    	Content    string
    	Recommends []string  // 문자열 배열
    	Comments   []Comment // Comment 구조체 배열
    }
    
    func main() {
    	data := make([]Article, 1) // 값을 저장할 구조체 슬라이스 생성
    
    	data[0].Id = 1
    	data[0].Title = "Hello, world!"
    	data[0].Author.Name = "Maria"
    	data[0].Author.Email = "maria@example.com"
    	data[0].Content = "Hello~"
    	data[0].Recommends = []string{"John", "Andrew"}
    	data[0].Comments = make([]Comment, 1)
    	data[0].Comments[0].Id = 1
    	data[0].Comments[0].Author.Name = "Andrew"
    	data[0].Comments[0].Author.Email = "andrew@hello.com"
    	data[0].Comments[0].Content = "Hello Maria"
    
    	doc, _ := json.Marshal(data) // data를 JSON 문서로 변환
    
    	fmt.Println(string(doc)) // [{"Id":1,"Title":"Hello, world!","Au... (생략)
    }


    패키지에서 함수(변수, 상수)를 외부에 노출할 때 이름의 첫 글자를 대문자로 만드는 것처럼 구조체도 같은 규칙으로 동작합니다. 즉 JSON 등으로 외부에 노출할 때는 구조체 필드의 첫 글자는 대문자로 만듭니다. 따라서 구조체 필드의 첫 글자를 소문자로 만들면 JSON 문서에 해당 필드는 포함되지 않습니다.

    구조체 필드가 대문자로 시작하면 JSON 문서 안의 키도 대문자로 시작하게 됩니다. 여기서 JSON 문서 안의 키를 소문자로 시작하고 싶다면 다음과 같이 구조체 필드에 태그를 지정해줍니다.

    // 필드명 자료형 `json:”키”`
    
    
    type 구조체명 struct {
    	필드명 자료형 `json:"키"`
    }


    type Author struct {
    	Name  string `json:"name"` // 구조체 필드에 태그 지정
    	Email string `json:"email"`
    }
    
    type Comment struct {
    	Id      uint64 `json:"id"`
    	Author  Author `json:"author"`
    	Content string `json:"content"`
    }
    
    type Article struct {
    	Id         uint64    `json:"id"`
    	Title      string    `json:"title"`
    	Author     Author    `json:"author"`
    	Content    string    `json:"content"`
    	Recommends []string  `json:"recommends"`
    	Comments   []Comment `json:"comments"`
    }

    태그는 문자열 형태이며 문자열 안에 " " (따옴표)가 포함되므로 ` ` (백쿼트)로 감싸줍니다. 그리고 JSON 문서이므로 `json:"키"` 형식으로 키 이름을 직접 지정합니다. 여기서 키 이름은 구조체 필드와 같을 필요는 없습니다.


    태그를 지정한 구조체로 JSON 문서를 만들면 다음과 같이 키를 소문자로 만들 수 있습니다.

    [
      {
        "id": 1,
        "title": "Hello, world!",
        "author": {
          "name": "Maria",
          "email": "maria@example.com"
        },
        "content": "Hello~",
        "recommends": [
          "John",
          "Andrew"
        ],
        "comments": [
          {
            "id": 1,
            "author": {
              "name": "Andrew",
              "email": "andrew@hello.com"
            },
            "content": "Hello Maria"
          }
        ]
      }
    ]


    구조체 태그는 JSON 문서를 만들거나, 타입 확인, 리플렉션(reflect 패키지)에서 사용할 수 있습니다


    댓글