GolangでJSONを使用する場合、空のフィールドと設定されていないフィールドをどのように区別しますか?

4654 ワード

文書ディレクトリ

  • 概要
  • 問題
  • 簡単な答え?ポインタ
  • 解決策
  • 付録:
  • 概要


    原文参照:GolangでJSONを使用する場合、空のフィールドと設定されていないフィールドをどのように区別しますか?数週間前、Golangマイクロサービスを利用していましたが、JSONデータを使用したCURP操作のサポートを追加する必要がありました.通常、エンティティにすべてのフィールドと「omitempty」プロパティを定義した構造体を作成します.
    type Article struct {
       Id   string      `json:"id"`
       Name string      `json:"name,omitempty"`
       Desc string      `json:"desc,omitempty"`
    }
    

    に質問


    しかし、このような表現形式は、特にUpdateやEditの動作に重大な問題をもたらす.
    例えば、更新要求のJSONデータがこのように見えるとする
    {"id":"1234","name":"xyz","desc":""}
    

    空のdescフィールドに注意してください.このリクエストデータがGoで解封された後どうなっているか見てみましょう
    func Test_JSON1(t *testing.T) {         
       jsonData:=`{"id":"1234","name":"xyz","desc":""}`
       req:=Article{}
       _=json.Unmarshal([]byte(jsonData),&req)
       fmt.Printf("%+v",req)
    }Output:
    === RUN   Test_JSON1
    {Id:1234 Name:xyz Desc:}
    

    ここでの説明は空の文字列であり、クライアントがdescを空の文字列に設定することを望んでいることは明らかである.これは私たちのプログラムによって推定されたものである.
    ただし、クライアントがDescの既存値を変更したくない場合、この場合、記述文字列を再送信するのは正しくないため、要求されたJSONデータはこのように見える可能性があります.
    {"id":"1234","name":"xyz"}
    

    構造体に解封しました
    func Test_JSON2(t *testing.T) {         
       jsonData:=`{"id":"1234","name":"xyz"}`
       req:=Article{}
       _=json.Unmarshal([]byte(jsonData),&req)
       fmt.Printf("%+v",req)
    }Output:
    === RUN   Test_JSON2
    {Id:1234 Name:xyz Desc:}
    

    ええ、Descは空の文字列として取得されますが、未設定のフィールドと空のフィールドを区別する方法

    簡単な答え?ししん


    解決策


    go-githubのような既存のGolangライブラリに啓発されました次のように、構造体フィールドをポインタタイプに変更できます.
    type Article struct {
       Id    string      `json:"id"`
       Name *string      `json:"name,omitempty"`
       Desc *string      `json:"desc,omitempty"`
    }
    

    これにより、フィールドに追加のステータスを追加しました.元のJSONにこのフィールドが存在しない場合、構造体フィールドは空になります(nil).
    一方、フィールドが存在する空の場合、ポインタは空ではなく、そのフィールドには空の値が含まれる.
    注意-私は「Id」フィールドをポインタタイプに変更していません.空の状態を備えていないため、idはデータベースのidのように必要です.
    もう一度やってみましょう.
    func Test_JSON_Empty(t *testing.T) {
       jsonData := `{"id":"1234","name":"xyz","desc":""}`
       req := Article{}
       _ = json.Unmarshal([]byte(jsonData), &req)
       fmt.Printf("%+v
    ", req) fmt.Printf("%s
    ", *req.Name) fmt.Printf("%s
    ", *req.Desc) } func Test_JSON_Nil(t *testing.T) { jsonData := `{"id":"1234","name":"xyz"}` req := Article{} _ = json.Unmarshal([]byte(jsonData), &req) fmt.Printf("%+v
    ", req) fmt.Printf("%s
    ", *req.Name) } Output === RUN Test_JSON_Empty {Id:1234 Name:0xc000088540 Desc:0xc000088550} Name: xyz Desc: --- PASS: Test_JSON_Empty (0.00s)=== RUN Test_JSON_Nil {Id:1234 Name:0xc00005c590 Desc:} Name: xyz --- PASS: Test_JSON_Nil (0.00s)

    1つ目のケースでは、descが空の文字列に設定されているため、Descでは空でないポインタと空の文字列を含む値を取得しました.2つ目は、このフィールドが設定されていないため、空の文字列ポインタが得られる.
    そのため、2つの更新を区別することができます.この方式は文字列だけでなく、整数、ネスト構造体など、他のすべてのデータ型にも適用できる.
    しかし、この方法にもいくつかの問題がある.
    ≪Nullセキュリティ|Null Security|emdw≫:ポインタ以外のデータ型は、固有のNullセキュリティを備えています.Golangでは、文字列または整数が空にならないことを意味します.デフォルト値は常に設定されています.ただし、ポインタが定義されている場合、これらのデータ型は手動で設定されていない場合はデフォルトで空です.したがって、空性を検証することなくポインタのデータにアクセスしようとすると、アプリケーションがクラッシュする可能性がある.
    #        ,    desc   
    func Test_JSON_Nil(t *testing.T) {
       jsonData := `{"id":"1234","name":"xyz"}`
       req := Article{}
       _ = json.Unmarshal([]byte(jsonData), &req)
       fmt.Printf("%+v
    ", req) fmt.Printf("%s
    ", *req.Desc) }

    空のポインタを常にチェックすることで、この問題を簡単に解決できますが、コードがうるさいように見えるかもしれません.
    印刷性:ポインタベースのソリューションの出力で気づいた問題のように、ポインタの値は印刷されません.20はポインタの16進数値を印刷します.これはアプリケーションでは役に立ちません.これはstringerインタフェースを再利用することによっても克服できる.
    func (a *Article) String() string {
       output:=fmt.Sprintf("Id: %s ",a.Id)
       if a.Name!=nil{
       output+=fmt.Sprintf("Name: '%s' ",*a.Name)
       }
       if u.Desc!=nil{
       output+=fmt.Sprintf("Desc: '%s' ",u.Desc)
       }
       return output
    }
    

    付録:


    上記の問題を解決するもう1つの方法は、ポインタに関心を持たずに空であるかどうかをチェックする方法を提供することができる空のタイプの三方ライブラリを使用することである.github.com/guregu/null github.com/google/go-github
    ——————————————————原文作者:wangchunbo転自リンク:https://learnku.com/go/t/49332著作権声明:著作権は作者の所有である.商業転載は著者に連絡して許可を得てください.非商業転載は以上の著者の情報と原文のリンクを残してください.