#Go json パッケージ
Marshal(): Go データ オブジェクト -> json データUnMarshal(): Json データ -> Go データ オブジェクトfunc Marshal(v interface{}) ([]byte, error) func Unmarshal(data []byte, v interface{}) error
JSON データの構築
Marshal() 関数と MarshalIndent() 関数では、次のことができます。データを json データにカプセル化します。 1. 構造体、スライス、配列、マップはすべて json に変換できます#2. 構造体を json に変換する場合、フィールドの最初の文字のみが変換されます
3. マップを変換するとき、キーは文字列である必要があります
4. カプセル化するとき、それがポインターの場合、ポインターが指すオブジェクトが追跡され、カプセル化されます
例:
構造体構造があります:
type Post struct { Id int Content string Author string }
この構造体は、記事 ID、記事の内容、記事の投稿者など、ブログ記事のタイプを表します。これについては何も言うことはありません。指定する必要があるのは、これが構造体であり、構造体は JSON データにカプセル化 (エンコード) できるということだけです。
この構造体データを json に変換するには、Marshal() を使用するだけです。次のように:
post := &Post{1, "Hello World", "userA"} b, err := json.Marshal(post) if err != nil { fmt.Println(nil) }
Marshal() は []byte 型を返します。変数 b には []byte 型の json データが格納されており、出力できます:
fmt.Println(string(b))
Result:
{"Id":1,"Content":"Hello World","Author":"userA"}
は、JSON にカプセル化するときに「美しく」できます。MarshalIndent() を使用して、プレフィックス (通常、プレフィックス文字列は空に設定されます) とインデントを自動的に追加します:
c,err := json.MarshalIndent(post,"","\t") if err != nil { fmt.Println(nil) } fmt.Println(string(c))
結果:
{ "Id": 1, "Content": "Hello World", "Author": "userA" }
構造体に加えて、配列、スライス、マップ構造もすべて json に解析できます。ただし、map を json に解析する場合、キーは json 構文で必要な文字列のみである必要があります。
例:
// slice -> json s := []string{"a", "b", "c"} d, _ := json.MarshalIndent(s, "", "\t") fmt.Println(string(d)) // map -> json m := map[string]string{ "a":"aa", "b":"bb", "c":"cc", } e,_ := json.MarshalIndent(m,"","\t") fmt.Println(string(e))
戻り結果:
[ "a", "b", "c" ] { "a": "aa", "b": "bb", "c": "cc" }
構造体タグを使用して json の構築を支援しますstruct 変換できるフィールドは先頭文字が大文字のすべてのフィールドですが、jsonで小文字で始まるキーを使いたい場合はstructのタグを使うことでリフレクションを補助することができます。
たとえば、Post 構造では、最初の文字が小文字のフィールド createAt が追加されます。
type Post struct { Id int `json:"ID"` Content string `json:"content"` Author string `json:"author"` Label []string `json:"label"` } postp := &Post{ 2, "Hello World", "userB", []string{"linux", "shell"}, } p, _ := json.MarshalIndent(postp, "", "\t") fmt.Println(string(p))
結果:
{ "ID": 2, "content": "Hello World", "author": "userB", "label": [ "linux", "shell" ] }
struct タグを使用する場合、いくつかの注意点があります:
1. タグ内で識別される名前は、 のキーの値と呼ばれます。 json データ
2. タグを `json:"-"` に設定すると、フィールド名の最初の文字が大文字であっても、このフィールドが json データに変換されないことを示すことができます
json キーの名前を文字 "-" にしたい場合は、`json:"-,"` を特別に処理できます。つまり、カンマを追加します。
3. タグに次のものが含まれている場合omitempty オプションを指定した場合、このフィールドの値が 0、つまり false、0、""、nil などの場合、このフィールドは json
4 に変換されません。フィールドが bool、string、int クラス、float クラスで、タグに string オプションが含まれている場合、このフィールドの値は json 文字列に変換されます
例:
type Post struct { Id int `json:"ID,string"` Content string `json:"content"` Author string `json:"author"` Label []string `json:"label,omitempty"` }
Json データを構造体に解析します (構造は既知です)Json データは構造体または空のインターフェースに解析できます。{} (スライス、マップ、等。)。上記の json を構築する際のタグのルールを理解すると、json を理解して解析するのは非常に簡単になります。
たとえば、次は json データの一部です:
{ "id": 1, "content": "hello world", "author": { "id": 2, "name": "userA" }, "published": true, "label": [], "nextPost": null, "comments": [{ "id": 3, "content": "good post1", "author": "userB" }, { "id": 4, "content": "good post2", "author": "userC" } ] }
この json データの一部を分析します:
1. 最上位の中括弧は匿名オブジェクトを表します。 Go にマッピングされています。構造体です。この構造体の名前は Post
2 であるとします。最上位の中括弧内のフィールドはすべて Post 構造体のフィールドです。これらのフィールドはすべて JSON データであるため、 , 最初の文字は大文字にする必要があり、タグも同時に設定する必要があります。タグ内の名前は小文字
3. Author はサブオブジェクトであり、Go の別の構造体にマップされます。 Post のこのフィールドの名前は Author です。名前が構造体名と同じであると仮定すると、Author
4 になります。ラベルは配列であり、Go ではスライスまたは配列にマッピングできます。また、json 配列は空であるため、Go のスライス/配列タイプは変数です。たとえば、int または string にすることも、interface{} にすることもできます。ここでの例では、ラベルは string でなければならないことがわかっています
5、nextPost はサブオブジェクトであり、Go の構造体にマッピングされますが、json のこのオブジェクトが null であるため、このオブジェクトが存在しないことを意味するため、Go の構造体にマッピングされる型決定することはできません。ただし、ここの例では、次の記事がないため、そのタイプも Post タイプ
6 である必要があります。コメントはサブオブジェクトであり、配列で囲まれています。Go にマッピングされ、スライスです。 /array、スライス/配列のタイプは構造体です
分析後、それに応じて構造体と構造体のタグを簡単に構築できます。上記の分析に基づいて構築されたデータ構造は次のとおりです。
type Post struct { ID int64 `json:"id"` Content string `json:"content"` Author Author `json:"author"` Published bool `json:"published"` Label []string `json:"label"` NextPost *Post `json:"nextPost"` Comments []*Comment `json:"comments"` } type Author struct { ID int64 `json:"id"` Name string `json:"name"` } type Comment struct { ID int64 `json:"id"` Content string `json:"content"` Author string `json:"author"` }
json データの構築を紹介するときに前述したように、ポインタは追跡されるため、構造体でポインタ型を使用する必要がないことに注意してください。ここで推測されます。疑わしいです。
そこで、この json データが .json ファイルに保存されていると仮定して、上記の json データを Post 型オブジェクトに解析します。コードは次のとおりです:
func main() { // 打开json文件 fh, err := os.Open("a.json") if err != nil { fmt.Println(err) return } defer fh.Close() // 读取json文件,保存到jsonData中 jsonData, err := ioutil.ReadAll(fh) if err != nil { fmt.Println(err) return } var post Post // 解析json数据到post中 err = json.Unmarshal(jsonData, &post) if err != nil { fmt.Println(err) return } fmt.Println(post) }
出力結果:
{1 hello world {2 userA} true [] <nil> [0xc042072300 0xc0420723c0]}
也许你已经感受到了,从json数据反推算struct到底有多复杂,虽然逻辑不难,但如果数据复杂一点,这是件非常恶心的事情。所以,使用别人写好的工具来自动转换吧。本文后面有推荐json到数据结构的自动转换工具。
解析json到interface(结构未知)
上面是已知json数据结构的解析方式,如果json结构是未知的或者结构可能会发生改变的情况,则解析到struct是不合理的。这时可以解析到空接口interface{}或map[string]interface{}类型上,这两种类型的结果是完全一致的。
解析到interface{}上时,Go类型和JSON类型的对应关系如下
JSON类型 Go类型 --------------------------------------------- JSON objects <--> map[string]interface{} JSON arrays <--> []interface{} JSON booleans <--> bool JSON numbers <--> float64 JSON strings <--> string JSON null <--> nil
例如:
func main() { // 读取json文件 fh, err := os.Open("a.json") if err != nil { fmt.Println(err) return } defer fh.Close() jsonData, err := ioutil.ReadAll(fh) if err != nil { fmt.Println(err) return } // 定义空接口接收解析后的json数据 var unknown interface{} // 或:map[string]interface{} 结果是完全一样的 err = json.Unmarshal(jsonData, &unknown) if err != nil { fmt.Println(err) return } fmt.Println(unknown) }
输出结果:
map[nextPost:<nil> comments:[map[id:3 content:good post1 author:userB] map[id:4 content:good post2 author:userC]] id:1 content:hello world author:map[id:2 name:userA] published:true label:[]]
上面将输出map结构。这是显然的,因为类型对应关系中已经说明了,json object解析到Go interface的时候,对应的是map结构。如果将上面输出的结构进行一下格式化,得到的将是类似下面的结构:
map[ nextPost:<nil> comments:[ map[ id:3 content:good post1 author:userB ] map[ id:4 content:good post2 author:userC ] ] id:1 content:hello world author:map[ id:2 name:userA ] published:true label:[] ]
现在,可以从这个map中去判断类型、取得对应的值。但是如何判断类型?可以使用类型断言:
func main() { // 读取json数据 fh, err := os.Open("a.json") if err != nil { fmt.Println(err) return } defer fh.Close() jsonData, err := ioutil.ReadAll(fh) if err != nil { fmt.Println(err) return } // 解析json数据到interface{} var unknown interface{} err = json.Unmarshal(jsonData, &unknown) if err != nil { fmt.Println(err) return } // 进行断言,并switch匹配 m := unknown.(map[string]interface{}) for k, v := range m { switch vv := v.(type) { case string: fmt.Println(k, "type: string\nvalue: ", vv) fmt.Println("------------------") case float64: fmt.Println(k, "type: float64\nvalue: ", vv) fmt.Println("------------------") case bool: fmt.Println(k, "type: bool\nvalue: ", vv) fmt.Println("------------------") case map[string]interface{}: fmt.Println(k, "type: map[string]interface{}\nvalue: ", vv) for i, j := range vv { fmt.Println(i,": ",j) } fmt.Println("------------------") case []interface{}: fmt.Println(k, "type: []interface{}\nvalue: ", vv) for key, value := range vv { fmt.Println(key, ": ", value) } fmt.Println("------------------") default: fmt.Println(k, "type: nil\nvalue: ", vv) fmt.Println("------------------") } } }
结果如下:
comments type: []interface{} value: [map[id:3 content:good post1 author:userB] map[author:userC id:4 content:good post2]] 0 : map[id:3 content:good post1 author:userB] 1 : map[id:4 content:good post2 author:userC] ------------------ id type: float64 value: 1 ------------------ content type: string value: hello world ------------------ author type: map[string]interface{} value: map[id:2 name:userA] name : userA id : 2 ------------------ published type: bool value: true ------------------ label type: []interface{} value: [] ------------------ nextPost type: nil value: <nil> ------------------
可见,从interface中解析非常复杂,而且可能因为嵌套结构而导致无法正确迭代遍历。这时候,可以使用第三方包simplejson,见后文。
解析、创建json流
除了可以直接解析、创建json数据,还可以处理流式数据。
1、type Decoder解码json到Go数据结构
2、ype Encoder编码Go数据结构到json
例如:
const jsonStream = ` {"Name": "Ed", "Text": "Knock knock."} {"Name": "Sam", "Text": "Who's there?"} {"Name": "Ed", "Text": "Go fmt."} {"Name": "Sam", "Text": "Go fmt who?"} {"Name": "Ed", "Text": "Go fmt yourself!"} ` type Message struct { Name, Text string } dec := json.NewDecoder(strings.NewReader(jsonStream)) for { var m Message if err := dec.Decode(&m); err == io.EOF { break } else if err != nil { log.Fatal(err) } fmt.Printf("%s: %s\n", m.Name, m.Text) }
输出:
Ed: Knock knock. Sam: Who's there? Ed: Go fmt. Sam: Go fmt who? Ed: Go fmt yourself!
再例如,从标准输入读json数据,解码后删除名为Name的元素,最后重新编码后输出到标准输出。
func main() { dec := json.NewDecoder(os.Stdin) enc := json.NewEncoder(os.Stdout) for { var v map[string]interface{} if err := dec.Decode(&v); err != nil { log.Println(err) return } for k := range v { if k != "Name" { delete(v, k) } } if err := enc.Encode(&v); err != nil { log.Println(err) } } }
更多go语言知识请关注PHP中文网go语言教程栏目。
以上がGo言語でjsonデータを処理する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。