[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으로 인코딩할 수 없습니다.
- 순환 데이터 구조는 지원하지 않습니다.
필드 태그
구조체 필드를 인코딩할 때 필드 태그를 사용하면 필드를 제외하거나 필드명을 변경하는 등 커스터마이징 할 수 있습니다.
필드 태그는 역 따옴표(`)로 감싸진 문자열이고 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}