GolangによるJSONシーケンス化

13726 ワード

総覧
JSONは最もポピュラーなシーケンス化フォーマットの一つです.これは人間が読むことができて、かなり簡潔で、どんなWebアプリケーションでもJavaScriptを使って簡単に解析することができます.Goは現代のプログラミング言語として、JSONシーケンス化を標準ライブラリで一流のサポートを提供しています.
しかし、ここには隅と隙間があります.このチュートリアルでは、JSONを効率的にシーケンス化する方法と、任意のデータと構造化されたデータをJSONからシーケンス化および逆シーケンス化する方法について説明します.シーケンス列挙などの高度なシーンの処理方法も学習します.
jsonパッケージ
Goは、その標準ライブラリの符号化パケットにおいて、複数のシーケンス化フォーマットをサポートする.その一つが流行しているJSON形式です.Marshal()関数を使用して、Golang値を1バイトスライスにシーケンス化できます.Unmarshal()関数を使用して、バイトのセグメントをGolang値に逆シーケンス化できます.これは簡単です.本明細書のコンテキストでは、次の用語は同等です.
  • シーケンス化/符号化/グループ
  • 逆シーケンス化/復号化/デグループ
  • 潜在的な階層化データ構造とバイトストリームを相互変換しているという事実を反映しているため、シーケンス化が好きです.
    元帥
    Marshal()関数は、Goで空のインタフェースを表し、バイトとエラーを返す任意の内容を受け入れることができます.これは署名です.func Marshal(v interface{}) ([]byte, error)
    Marshal()が入力値をシーケンス化できない場合、nil以外のエラーが返されます.Marshal()にはいくつかの厳しい制限があります(カスタムグループを使用して克服する方法は後述します):
  • マッピングキーは文字列でなければなりません.
  • マッピング値は、jsonパケットがシーケンス可能なタイプである必要があります.
  • では、チャネル、複雑さ、および機能はサポートされていません.
  • では、循環データ構造はサポートされていません.
  • ポインタは、ポインタが指す値(ポインタがnilの場合はnull)として符号化(復号)される.
  • 元帥
    Unmarshal()関数は、通常、構造または基本タイプのポインタを指す有効なJSONとターゲットインタフェースを表すことが期待されるバイトスライスを使用します.JSONをインタフェースに逆シーケンス化します.シーケンス化に失敗すると、エラーが返されます.署名です.func Unmarshal(data []byte, v interface{}) error
    単純タイプのシーケンス化
    jsonパッケージを使用するなど、簡単なタイプを簡単にシーケンス化できます.結果は完全なJSONオブジェクトではなく、単純な文字列になります.ここで、int 5は、文字列「5」に対応するバイト配列にシーケンス化される[53].
    // Serialize int
        var x = 5
        bytes, err := json.Marshal(x)
        if err != nil {
            fmt.Println("Can't serislize", x)
        }
    
        fmt.Printf("%v => %v, '%v'
    ", x, bytes, string(bytes)) // Deserialize int var r int err = json.Unmarshal(bytes, &r) if err != nil { fmt.Println("Can't deserislize", bytes) } fmt.Printf("%v => %v
    ", bytes, r) Output: - 5 => [53], '5' - [53] => 5

    サポートされていないタイプ(関数など)をシーケンス化しようとすると、エラーメッセージが表示されます.
    // Trying to serialize a function
        foo := func() {
            fmt.Println("foo() here")
        }
    
        bytes, err = json.Marshal(foo)
        if err != nil {
            fmt.Println(err)
        }
    
    Output:
    
    json: unsupported type: func()

    地図を使用して任意のデータをシーケンス化
    JSONの強みは,任意の階層データをうまく表現できることである.JSONパケットはこれをサポートしており、JSON階層を表すために共通のNullインタフェースを使用しています.これは逆シーケンス化され、その後にツリーがシーケンス化される例で、各ノードにはint値と左右の2つのブランチがあり、別のノードまたはnullが含まれる場合があります.
    JSON空の値はGo nilに等しい.出力から、json.Unmarshal()関数は、ネストされたインタフェースマッピングからなり、値タイプをintに保持するJSON blobをGoデータ構造に変換することに成功した.json.Marshal()関数は、得られたネストされたオブジェクトを同じJSON表現にシーケンス化することに成功した.
    // Arbitrary nested JSON
        dd := `
        {
            "value": 3,
            "left": {
                "value": 1,
                "left": null,
                "right": {
                    "value": 2,
                    "left": null,
                    "right": null
                }
            },
            "right": {
                "value": 4,
                "left": null,
                "right": null
             }
        }`
    
        var obj interface{}
        err = json.Unmarshal([]byte(dd), &obj)
        if err != nil {
            fmt.Println(err)
        } else {
            fmt.Println("--------
    ", obj) } data, err = json.Marshal(obj) if err != nil { fmt.Println(err) } else { fmt.Println("--------
    ", string(data)) } } Output: -------- map[right:map[value:4 left: right:] value:3 left:map[left: right:map[value:2 left: right:] value:1]] -------- {"left":{ "left":null, "right":{"left":null,"right":null,"value":2}, "value":1}, "right":{"left":null, "right":null, "value":4}, "value":3}

    インタフェースの汎用マッピングを巡回するには、タイプブレークスルーを使用する必要があります.例:
    func dump(obj interface{}) error {
        if obj == nil {
            fmt.Println("nil")
            return nil
        }
        switch obj.(type) {
        case bool:
            fmt.Println(obj.(bool))
        case int:
            fmt.Println(obj.(int))
        case float64:
            fmt.Println(obj.(float64))
        case string:
            fmt.Println(obj.(string))
        case map[string]interface{}:
            for k, v := range(obj.(map[string]interface{})) {
                fmt.Printf("%s: ", k)
                err := dump(v)
                if err != nil {
                    return err
                }
            }
        default:
            return errors.New(
                fmt.Sprintf("Unsupported type: %v", obj))
        }
    
        return nil
    }

    構造化データのシーケンス化
    構造化データの使用は、通常より良い選択です.囲碁はJSONシーケンス化から/素晴らしいサポートstructsまでのstructのラベルを提供しています.JSONツリーに対応するstructと、よりスマートなDump()関数を作成して印刷します.
    type Tree struct {
        value int
        left *Tree
        right *Tree
    }
    
    func (t *Tree) Dump(indent string) {
        fmt.Println(indent + "value:", t.value)
        fmt.Print(indent + "left: ")
        if t.left == nil {
            fmt.Println(nil)
        } else {
            fmt.Println()
            t.left.Dump(indent + "  ")
        }
    
        fmt.Print(indent + "right: ")
        if t.right == nil {
            fmt.Println(nil)
        } else {
            fmt.Println()
            t.right.Dump(indent + "  ")
        }
    }

    任意のJSON方法に比べて、これは素晴らしいし、きれいです.しかし、これは有効ですか?違います.エラーはありませんが、JSONは私たちのツリーオブジェクトを埋めていません.
    jsonTree := `
        {
            "value": 3,
            "left": {
                "value": 1,
                "left": null,
                "right": {
                    "value": 2,
                    "left": null,
                    "right": null
                }
            },
            "right": {
                "value": 4,
                "left": null,
                "right": null
             }
        }`
    
    
        var tree Tree
        err = json.Unmarshal([]byte(dd), &tree)
        if err != nil {
            fmt.Printf("- Can't deserislize tree, error: %v
    ", err) } else { tree.Dump("") } Output: value: 0 left: right:

    問題は、ツリーフィールドがプライベートであることです.JSONシーケンス化は共通フィールドにのみ適用されます.したがって、structフィールドを開示することができる.jsonパッケージは、小文字キー「value」、「left」、「right」を対応する大文字フィールド名に透明に変換できるほどスマートです.
    type Tree struct {
        Value int   `json:"value"`
        Left  *Tree `json:"left"`
        Right *Tree `json:"right"`
    }
    
    
    Output:
    
    value: 3
    left: 
      value: 1
      left: 
      right: 
        value: 2
        left: 
        right: 
    right: 
      value: 4
      left: 
      right: 

    jsonパケットは、JSONのマッピングされていないフィールドおよびstructプライベートフィールドを黙々と無視します.ただし、JSONの特定のキーをstructの異なる名前のフィールドにマッピングする場合があります.このためにstructタグを使用できます.たとえば、JSONに「ラベル」という別のフィールドを追加したとしますが、構造内で「Tag」というフィールドにマッピングする必要があります.
    type Tree struct {
        Value int
        Tag string    `json:"label"`
        Left  *Tree
        Right *Tree
    }
    
    func (t *Tree) Dump(indent string) {
        fmt.Println(indent + "value:", t.Value)
        if t.Tag != "" {
            fmt.Println(indent + "tag:", t.Tag)
        }
        fmt.Print(indent + "left: ")
        if t.Left == nil {
            fmt.Println(nil)
        } else {
            fmt.Println()
            t.Left.Dump(indent + "  ")
        }
    
        fmt.Print(indent + "right: ")
        if t.Right == nil {
            fmt.Println(nil)
        } else {
            fmt.Println()
            t.Right.Dump(indent + "  ")
        }
    }

    これは新しいJSONで、ツリーのルートノードは「root」とマークされ、Tagフィールドに正しくシーケンス化され、出力に印刷されています.
    dd := `
        {
            "label": "root",
            "value": 3,
            "left": {
                "value": 1,
                "left": null,
                "right": {
                    "value": 2,
                    "left": null,
                    "right": null
                }
            },
            "right": {
                "value": 4,
                "left": null,
                "right": null
             }
        }`
    
    
        var tree Tree
        err = json.Unmarshal([]byte(dd), &tree)
        if err != nil {
            fmt.Printf("- Can't deserislize tree, error: %v
    ", err) } else { tree.Dump("") } Output: value: 3 tag: root left: value: 1 left: right: value: 2 left: right: right: value: 4 left: right:

    カスタムグループの作成
    Marshal()関数の要件を満たしていないオブジェクトをシーケンス化する必要があることがよくあります.たとえば、intキーを使用してマッピングをシーケンス化したい場合があります.この場合、MarshalerおよびUnmarshalerインタフェースを実装することで、カスタム・グループ化/解Unmarshalerを記述できます.
    スペルに関する注意事項:Goでは、メソッド名の後に「er」接尾辞を付けることで、インタフェースを単一のメソッドで命名することを約束します.したがって、より一般的なスペルが「Marshaller」(ダブルL)であっても、インタフェース名は「Marshaler」(シングルL)にすぎない.
    以下に、MarshalerインタフェースとUnmarshalerインタフェースを示します.
    type Marshaler interface {
            MarshalJSON() ([]byte, error)
    }
    
    type Unmarshaler interface {
            UnmarshalJSON([]byte) error
    }

    組み込みタイプまたは組み込みタイプの組合せ(たとえばmap[int]string)をシーケンス化する場合でも、カスタムシーケンス化時にタイプを作成する必要があります.ここでは、タイプIntStringMapという名前のインタフェースを定義し、MarshalerおよびUnmarshalerを実装します.MarshalJSON()メソッドは、自身のint鍵を文字列に変換し、標準のmap[string]string関数を使用して文字列鍵を使用して地図をシーケンス化するjson.Marshal()を作成する.
    type IntStringMap map[int]string
    
    func (m *IntStringMap) MarshalJSON() ([]byte, error) {
        ss := map[string]string{}
        for k, v := range *m {
            i := strconv.Itoa(k)
            ss[i] = v
        }
        return json.Marshal(ss)
    }

    UnmarshalJSON()メソッドの役割は正反対です.データバイト配列をmap[string]stringに逆シーケンス化し、各文字列キーをintに変換して埋め込みます.
    func (m *IntStringMap) UnmarshalJSON(data []byte ) error {
        ss := map[string]string{}
        err := json.Unmarshal(data, &ss)
        if err != nil {
            return err
        }
        for k, v := range ss {
            i, err := strconv.Atoi(k)
            if err != nil {
                return err
            }
            (*m)[i] = v
        }
        return nil
    }

    これはプログラムで使用する方法です.
    m := IntStringMap{4: "four", 5: "five"}
        data, err := m.MarshalJSON()
        if err != nil {
            fmt.Println(err)
        }
        fmt.Println("IntStringMap to JSON: ", string(data))
    
    
        m = IntStringMap{}
    
        jsonString := []byte("{\"1\": \"one\", \"2\": \"two\"}")
        m.UnmarshalJSON(jsonString)
    
        fmt.Printf("IntStringMap from JSON: %v
    ", m) fmt.Println("m[1]:", m[1], "m[2]:", m[2]) Output: IntStringMap to JSON: {"4":"four","5":"five"} IntStringMap from JSON: map[2:two 1:one] m[1]: one m[2]: two

    シーケンス列挙
    Go列挙のシーケンス化は面倒かもしれません.Go jsonのシーケンス化に関する文章を書く考えは、同僚が列挙をどのようにシーケンス化するかについて私に聞いたことから来ています.これはGo enumです.定数ゼロと1は整数0と1に等しい.
    type EnumType int
    
    const (
        Zero     EnumType = iota
        One
    )

    intと考えられ、多くの点で可能ですが、直接シーケンス化することはできません.カスタム封送解体/解体解体解体を作成する必要があります.前節の後、これは問題ではありません.次のMarshalJSON()およびUnmarshalJSON()は、定数ZEROおよびONEを対応する文字列「Zero」および「One」/に逆シーケンス化する.
    func (e *EnumType) UnmarshalJSON(data []byte) error {
        var s string
        err := json.Unmarshal(data, &s)
        if err != nil {
            return err
        }
    
        value, ok := map[string]EnumType{"Zero": Zero, "One": One}[s]
        if !ok {
            return errors.New("Invalid EnumType value")
        }
        *e = value
        return nil
    }
    
    func (e *EnumType) MarshalJSON() ([]byte, error) {
        value, ok := map[EnumType]string{Zero: "Zero", One:"One"}[*e]
        if !ok {
            return nil, errors.New("Invalid EnumType value")
        }
        return json.Marshal(value)
    }

    このEnumTypestructに埋め込み、シーケンス化してみましょう.main関数はEnumContainerを作成し、int 1に等しい名前「Uno」およびenum定数ONEの値に初期化します.
    type EnumContainer struct {
        Name                string
        Value               EnumType
    }
    
    
    func main() {
        x := One
        ec := EnumContainer{
            "Uno",
            x,
        }
        s, err := json.Marshal(ec)
        if err != nil {
            fmt.Printf("fail!")
        }
    
        var ec2 EnumContainer
        err = json.Unmarshal(s, &ec2)
    
        fmt.Println(ec2.Name, ":", ec2.Value)
    }
    
    Output:
    
    Uno : 0

    予想される出力は「Uno:1」ですが、「Uno:0」です.何があったの?グループ/グループ解除コードにエラーはありません.列挙をシーケンス化する場合は、列挙を値で埋め込むことはできません.列挙を指すポインタを埋め込む必要があります.これは、予定通りに作業できる修正バージョンです.
    type EnumContainer struct {
        Name                string
        Value               *EnumType
    }
    
    func main() {
        x := One
        ec := EnumContainer{
            "Uno",
            &x,
        }
        s, err := json.Marshal(ec)
        if err != nil {
            fmt.Printf("fail!")
        }
    
        var ec2 EnumContainer
        err = json.Unmarshal(s, &ec2)
    
        fmt.Println(ec2.Name, ":", *ec2.Value)
    }
    
    Output:
    
    Uno : 1

    結論
    Goは、JSONをシーケンス化および逆シーケンス化するための多くのオプションを提供する.符号化/jsonパケットのいきさつを理解し,その機能を利用することが重要である.
    このチュートリアルでは、分かりにくいGo列挙をシーケンス化する方法など、すべての機能を把握します.
    いくつかのオブジェクトをシーケンス化します!
    翻訳:https://code.tutsplus.com/tutorials/json-serialization-with-golang--cms-30209