ホームページ バックエンド開発 Golang Go言語でjsonデータを処理する方法

Go言語でjsonデータを処理する方法

Jan 11, 2020 pm 05:14 PM
言語を移動

Go言語でjsonデータを処理する方法

#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&#39;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&#39;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 サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

GOの浮動小数点番号操作に使用されるライブラリは何ですか? GOの浮動小数点番号操作に使用されるライブラリは何ですか? Apr 02, 2025 pm 02:06 PM

GO言語の浮動小数点数操作に使用されるライブラリは、精度を確保する方法を紹介します...

GOのどのライブラリが大企業によって開発されていますか、それとも有名なオープンソースプロジェクトによって提供されていますか? GOのどのライブラリが大企業によって開発されていますか、それとも有名なオープンソースプロジェクトによって提供されていますか? Apr 02, 2025 pm 04:12 PM

大企業または有名なオープンソースプロジェクトによって開発されたGOのどのライブラリが開発されていますか? GOでプログラミングするとき、開発者はしばしばいくつかの一般的なニーズに遭遇します...

Go's Crawler Collyのキュースレッドの問題は何ですか? Go's Crawler Collyのキュースレッドの問題は何ですか? Apr 02, 2025 pm 02:09 PM

Go Crawler Collyのキュースレッドの問題は、Go言語でColly Crawler Libraryを使用する問題を調査します。 �...

Goでは、Printlnとstring()関数を備えた文字列を印刷すると、なぜ異なる効果があるのですか? Goでは、Printlnとstring()関数を備えた文字列を印刷すると、なぜ異なる効果があるのですか? Apr 02, 2025 pm 02:03 PM

Go言語での文字列印刷の違い:printlnとstring()関数を使用する効果の違いはGOにあります...

GoおよびViperライブラリを使用するときにポインターを渡す必要があるのはなぜですか? GoおよびViperライブラリを使用するときにポインターを渡す必要があるのはなぜですか? Apr 02, 2025 pm 04:00 PM

ポインター構文とviperライブラリの使用における問題への取り組みGO言語でプログラミングするとき、特にポインターの構文と使用を理解することが重要です...

GO言語の「VAR」と「タイプ」キーワード定義構造の違いは何ですか? GO言語の「VAR」と「タイプ」キーワード定義構造の違いは何ですか? Apr 02, 2025 pm 12:57 PM

GO言語で構造を定義する2つの方法:VARとタイプのキーワードの違い。構造を定義するとき、GO言語はしばしば2つの異なる執筆方法を見ます:最初...

Redisストリームを使用してGO言語でメッセージキューを実装する場合、user_idタイプの変換の問題を解決する方法は? Redisストリームを使用してGO言語でメッセージキューを実装する場合、user_idタイプの変換の問題を解決する方法は? Apr 02, 2025 pm 04:54 PM

redisstreamを使用してGo言語でメッセージキューを実装する問題は、GO言語とRedisを使用することです...

GO言語の範囲を使用してマップを通過してマップを保存するのに、なぜすべての値が最後の要素になるのですか? GO言語の範囲を使用してマップを通過してマップを保存するのに、なぜすべての値が最後の要素になるのですか? Apr 02, 2025 pm 04:09 PM

GOのマップイテレーションにより、すべての値が最後の要素になるのはなぜですか? Go言語では、いくつかのインタビューの質問に直面したとき、あなたはしばしば地図に遭遇します...

See all articles