GoでJSONを使用する場合、空フィールドと未設定フィールドの区別方法
4592 ワード
微信は「呉親強の深夜食堂」を検索し、公衆番号に注目し、「微信」に返信し、友达と一緒に勉強した.
数週間前、JSONデータのCRUDサポートを追加する必要があるGolangベースのマイクロサービスプロジェクトを開発していました.
通常、定義されたすべてのフィールドとomitemptyプロパティを含むエンティティの構造を構築します.
しかし、この表現は特にUpdateまたはEdit操作に深刻な問題をもたらす.
例えば、更新要求のJSONがそうであるとすると、
空のdescフィールドに注意してください.では、Goでどのように分解されているかを見てみましょう.
ここのdescは空の文字列です.明らかに、クライアントはdescを空の文字列に設定することを望んでいます.これは私たちのプログラムによって推定されます.
ただし、クライアントがdescの既存値を変更したくない場合は、desc文字列を再送信するのは正しくありません.したがって、要求されたJSONデータは、
私たちの構造に分解し
私たちは依然としてdescを空の文字列としていますが、未設定フィールドと空のフィールドをどのように区別しますか?
短い答えはポインタです.
ソリューションというインスピレーションは、go-githubなどの既存のGolangライブラリから来ています.構造体フィールドをポインタタイプに変更できます.
これにより、フィールドに追加のステータスを追加しました.このフィールドが元の要求JSONデータに存在しない場合、null(nil)となります.
一方、フィールドが確かに存在し、値が空の場合、ポインタは空ではなく、フィールドには空の値が含まれます.
注意-Idはポインタタイプに変更されていません.そのため、空のステータスを持つことはできません.IdはデータベースIdと同様に常に存在する必要があります.やってみましょう.
出力、
第1の場合、descが空の文字列に設定されると、空でないポインタが得られ、その値は空の文字列である.2つ目の場合、フィールドが設定されていない場合、空のポインタが得られます.
そのため、この2つの更新を区別することができます.この方法は文字列タイプだけでなくint,ネストstructなど,他のタイプにも適用できる.
しかし、この方法はいくつかの問題をもたらします.
≪空のセキュリティ|Null Security|emdw≫:ポインタ以外のデータ型には固有の空のセキュリティがあります.これは、stringまたはintがGolangでnullになることはありません.常にデフォルト値があります.ただし、ポインタが定義されると、手動で設定されていない場合、これらのデータ型はnullにデフォルト設定されます.したがって、空性を検証せずにこれらのポインタデータにアクセスしようとすると、アプリケーションがクラッシュする可能性があります.
空のポインタを常にチェックすることで、この問題は簡単に回避できますが、コードが優雅に見えない可能性があります.
印刷性:ご覧のように、ポインタの値は印刷されません.逆に、16進数ポインタが印刷されるので、アプリケーションではあまり役に立ちません.これはstringerインタフェースを実現することによって克服できる.
付録のもう1つの解決策は、ポインタを気にすることなく、外部ライブラリを使用して空のタイプを持つことです.これらのタイプは、ポインタが空であるかどうかを確認する方法を提供します.
https://github.com/guregu/null
https://github.com/google/go-github
実用的ではないでしょうか.他に解決策はありますか?以下のメッセージを歓迎します.
もし文章があなたに役に立つならば、いいね、転送、伝言はすべて1種の支持です!
数週間前、JSONデータのCRUDサポートを追加する必要があるGolangベースのマイクロサービスプロジェクトを開発していました.
通常、定義されたすべてのフィールドと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を空の文字列に設定することを望んでいます.これは私たちのプログラムによって推定されます.
ただし、クライアントが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データに存在しない場合、null(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("%+vn", req)
fmt.Printf("%sn", *req.Name)
fmt.Printf("%sn", *req.Desc)
}
func Test_JSON_Nil(t *testing.T) {
jsonData := `{"id":"1234","name":"xyz"}`
req := Article{}
_ = json.Unmarshal([]byte(jsonData), &req)
fmt.Printf("%+vn", req)
fmt.Printf("%sn", *req.Name)
}
出力、
=== 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が空の文字列に設定されると、空でないポインタが得られ、その値は空の文字列である.2つ目の場合、フィールドが設定されていない場合、空のポインタが得られます.
そのため、この2つの更新を区別することができます.この方法は文字列タイプだけでなくint,ネストstructなど,他のタイプにも適用できる.
しかし、この方法はいくつかの問題をもたらします.
≪空のセキュリティ|Null Security|emdw≫:ポインタ以外のデータ型には固有の空のセキュリティがあります.これは、stringまたはintがGolangでnullになることはありません.常にデフォルト値があります.ただし、ポインタが定義されると、手動で設定されていない場合、これらのデータ型はnullにデフォルト設定されます.したがって、空性を検証せずにこれらのポインタデータにアクセスしようとすると、アプリケーションがクラッシュする可能性があります.
#The following code will crash because desc is null
func Test_JSON_Nil(t *testing.T) {
jsonData := `{"id":"1234","name":"xyz"}`
req := Article{}
_ = json.Unmarshal([]byte(jsonData), &req)
fmt.Printf("%+vn", req)
fmt.Printf("%sn", *req.Desc)
}
空のポインタを常にチェックすることで、この問題は簡単に回避できますが、コードが優雅に見えない可能性があります.
印刷性:ご覧のように、ポインタの値は印刷されません.逆に、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 a.Desc!=nil{
output+=fmt.Sprintf("Desc: '%s' ",a.Desc)
}
return output
}
付録のもう1つの解決策は、ポインタを気にすることなく、外部ライブラリを使用して空のタイプを持つことです.これらのタイプは、ポインタが空であるかどうかを確認する方法を提供します.
https://github.com/guregu/null
https://github.com/google/go-github
実用的ではないでしょうか.他に解決策はありますか?以下のメッセージを歓迎します.
もし文章があなたに役に立つならば、いいね、転送、伝言はすべて1種の支持です!