yoongrammer

[Go] JSON 패키지 알아보기 본문

언어/Go 언어

[Go] JSON 패키지 알아보기

yoongrammer 2021. 1. 2. 10:45
728x90

목차

[Go] JSON 패키지 알아보기


JSON (JavaScript Object Notation)은 간단한 데이터 교환 형식입니다.

 

json 패키지를 사용하면 go프로그램에서 JSON 데이터를 읽고 쓸 수 있습니다.

Encoding


JSON 데이터를 인코딩하기 위해서는 Marshal 함수를 사용합니다.

func Marshal(v interface{}) ([]byte, error)

Marshal 함수는 v를 인코딩한 JSON 값을 반환합니다.

 

Marshal 함수는 아래와 같은 규칙으로 데이터를 JSON으로 인코딩합니다.

- Boolean 값은 JSON boolean 값으로 인코딩 됩니다.

bolB, _ := json.Marshal(true)  // true

 

- Number(int, float)값은 JSON number로 인코딩 됩니다.

intB, _ := json.Marshal(1)    // 1
fltB, _ := json.Marshal(3.14) // 3.14

 

- string은 JSON string으로 인코딩 됩니다.

v, _ := json.Marshal("apple") // "apple"

 

- Array 와 slice 값은 JSON array로 인코딩 됩니다.

slcB, _ := json.Marshal([]string{"apple", "peach", "pear")
// ["apple","peach","pear"]

 

- struct 값은 JSON object로 인코딩 됩니다.

- 구조체 필드명이 소문자로 시작하는 경우 인코딩에서 제외됩니다.

v, _ := json.Marshal(struct{Page int; Fruits []string}{10,[]string{"apple","peach"})
// {"Page":10,"Fruits":["apple","peach"]}

 

- map 값은 JSON object로 인코딩 됩니다. 

- map 유형을 인코딩하려면 반드시 map[string]T 형식이어야 합니다.

- 여기서 T는 json package에서 재공되는 go type입니다.

mapB, _ := json.Marshal(map[string]int{"apple": 5, "lettuce": 7})
// {"apple":5,"lettuce":7}

 

- 포인터는 가리키는 값으로 인코딩 됩니다.

var num int = 15
var numPtr *int = &num
pB, _ := json.Marshal(numPtr) // 15

 

- 인터페이스 값은 인터페이스에 포함된 값으로 인코딩 됩니다. 

v, _ := json.Marshal(interface{}(10))
// 10

 

- nill 값은 null값으로 인코딩 됩니다.

v, _ := json.Marshal(nil)
// null

 

- Channel, complex 및 함숫값은 JSON으로 인코딩할 수 없습니다.

- 순환 데이터 구조는 지원하지 않습니다.

728x90

필드 태그


구조체 필드를 인코딩할 때 필드 태그를 사용하면 필드를 제외하거나 필드명을 변경하는 등 커스터마이징 할 수 있습니다.

 

필드 태그는 역 따옴표(`)로 감싸진 문자열이고 json key로 시작합니다.

필드 태그가 없다면 구조체 필드명으로 인코딩 됩니다.

 

예시:

type User struct {
  Name    string `json:"myName"` 
  Age     int   
}

v, _ := json.Marshal(User{"Tom", 20}) 
// {"myName":"Tom", "Age":20} <- Name이 아닌 태그에 있는 myName으로 인코딩 됨.

태그에 컴마(,)를 사용하여 필드에 옵션을 추가할 수 있습니다.

 

옵션 설명:

omitempty

- 필드가 빈 값(false, 0, nil pointer, nil interface, empty array, slice, map or string)인 경우 인코딩에서 생략됩니다.

type User struct{
  Name string `json:"myName"`
  Age int `json:"age,omitempty"`
}

v, _= json.Marshal(User{"Tom", 0})
// {"myName":"Tom"} <- Age 필드 값이 빈 값(0)이기 때문에 인코딩에서 제외됨

string

- 필드 값을 string으로 인코딩합니다.

- 문자열, 부동 소수점, 정수 또는 부울 유형의 필드에만 적용됩니다.

type User struct{
  Name string `json:"myName"`
  Age int `json:"age,string"`
}

v, _= json.Marshal(User{"Tom", 20})
// {"myName":"Tom","age":"20"} <- int 값이 string으로 인코딩 됨

 

태그 값이 -인 경우, 해당 필드는 인코딩에서 항상 제외됩니다.

type User struct{
  Name string `json:"myName"`
  Age int `json:"-"`
}

v, _= json.Marshal(User{"Tom", 20})
// {"myName":"Tom"} <- Age 필드 값이 인코딩에서 제외됨

 

- 값을 필드명으로 사용하고 싶은 경우 -, 태그를 사용하면 됩니다.

type User struct{
  Name string `json:"myName"`
  Age int `json:"-,"`
}

v, _= json.Marshal(User{"Tom", 20})
// {"myName":"Tom","-":20}

Decoding


JSON 데이터를 디코딩하기 위해서는 Unmarshal 함수를 사용합니다.

func Unmarshal(data []byte, v interface{}) error

Unmarshal은 JSON으로 인코딩 된 데이터를 구문 분석하고 결과를 v가 가리키는 값에 저장합니다.

 

Unmarshal은 디코딩된 데이터를 저장하기 위한 필드를 식별하기 위해 다음과 같은 규칙을 따릅니다.

  • 주어진 JSON 객체키와 디코딩하기 위한 구조체 필드이름 또는 태그와 같아야 합니다.
  • 대소 문자를 구분하지 않습니다. (하지만 대문자로 시작해야 됨)
  • 구조체 필드에 없는 JSON 키는 무시됩니다.
type User struct{
  Name string `json:"myName"`
  Age int 
}

JSONBlob := []byte(`{"myName":"Bob", "Age":10, "Number":3}`)
var u User
err := json.Unmarshal(JSONBlob, &u)
fmt.Printf("%+v", u) // {Name:Bob Age:10}

 

JSON 데이터 구조를 모른다면 빈 인터페이스(interface {})를 사용합니다.

JSONBlob := []byte(`{"myName":"Bob", "Age":10}`)
var u interface{}
err := json.Unmarshal(JSONBlob, &u)
fmt.Printf("%+v", u) // map[Age:10 Name:Bob]

JSON을 인터페이스 값으로 역 정렬화 하기 위해 다음 중 하나를 인터페이스 값에 저장합니다.

  • JSON 객체 → map[string]interface{}
  • JSON 배열 → []interface{}
  • 나머지 → interface{}
    • JSON booleans→ bool
    • JSON numbers → float64
    • JSON strings→ string
    • JSON null → nil

type assertion을 사용하여 데이터에 접근할 수 있습니다.

user := u.(map[string]interface{})
fmt.Println(user["Age"])

깔끔한 출력


JSON은 일반적으로 추가 공백 없이 하나의 긴 바이트로 저장됩니다.

MarshalIndent


MarshalIndent 함수를 사용하면 들여 쓰기를 설정하여 출력 형식을 정할 수 있습니다.

func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)

prefix 인자는 모든 라인에 쓸 문자를 지정하고 indent는 들여 쓰기에 사용은 문자를 지정합니다.

 

사용 예:

data := map[string]int{
		"a": 1,
		"b": 2,
	}

  json, _ := json.Marshal(data)
// output: 
// {"a":1,"b":2}


	json, _ := json.MarshalIndent(data, "<prefix>", "<indent>")
// output:
// {
//  <prefix><indent>"a": 1,
//  <prefix><indent>"b": 2
//  <prefix>}

  json, _ := json.MarshalIndent(data,"","  ")
// output:
// {
//   "a": 1,
//   "b": 2
// }

보통 prefix는 사용하지 않고 indent는 2-space 또는 을 많이 사용합니다.

Compact


들여 쓰기 함수의 반대는 Compact 함수입니다.

func Compact(dst *bytes.Buffer, src []byte) error

JSON 인코딩 된 src에 공백 문자를 제거하여 dst에 추가합니다.

 

사용 예:

data := map[string]int{
		"a": 1,
		"b": 2,
	}

	b, _ := json.MarshalIndent(data, "", "  ")
	fmt.Println(string(b))
// output:
// {
//   "a": 1,
//   "b": 2
// }

	var out bytes.Buffer
	json.Compact(&out, b)

	out.WriteTo(os.Stdout)
// output:
// {"a":1,"b":2}

 

참고:

golang.org/pkg/encoding/json/

blog.golang.org/json

medium.com/go-walkthrough/go-walkthrough-encoding-json-package-9681d1d37a8f#.eszbi1cjw

728x90

'언어 > Go 언어' 카테고리의 다른 글

[Go] go get 알아보기  (0) 2021.01.11
[Go] context 사용하기  (0) 2020.12.14
[Go] context 알아보기  (0) 2020.12.13
[Go] Modules 사용하기  (0) 2020.12.07
[Go] Modules 알아보기  (0) 2020.12.06
Comments