Go jsonデータの処理
14502 ワード
jsonデータフォーマット
jsonデータフォーマットの説明を参照してください.
jsonデータを操作したことがない場合は、上記の文章を見て、本稿の後の内容を理解するのに役立つことをお勧めします.
Go jsonパッケージ
Marshal():Goデータオブジェクト->jsonデータUnMarshal():Jsonデータ->Goデータオブジェクト
jsonデータの構築
Marshal()およびMarshalIndent()関数は、データをjsonデータにカプセル化することができる. struct、slice、array、mapはjson に変換できます. structがjsonに変換されると、フィールドの頭文字が大文字のものだけが に変換されます. map変換の場合、keyはstring でなければなりません.がカプセル化する場合、ポインタであれば、ポインタが指すオブジェクトを追跡してカプセル化 を行う.
例:
struct構造があります.
この構造はブログ記事タイプを表しており,記事ID,記事内容,記事の提出者がある.これは何も言うことはありません.唯一示す必要があるのは、structであり、structはJSONデータにカプセル化(符号化)することができます.
このstructデータをjsonに変換するには、Marshal()を使用します.次のようになります.
Marshal()は
結果:
jsonにカプセル化するときに「美化」を行い、MarshalIndent()を使用すると、接頭辞(接頭辞文字列は一般的に空に設定されています)とインデントを自動的に追加できます.
結果:
structを除いてarray、slice、map構造はjsonに解析できますが、mapがjsonに解析される場合、keyはstringのみでなければなりません.これはjson文法の要求です.
例:
結果を返します.
struct tagを使用してjsonの構築を支援
structが変換できるフィールドはすべて頭文字の大文字のフィールドですが、jsonで小文字の先頭のkeyを使用するにはstructのtagを使用して反射を補助することができます.
たとえば、Post構造は、頭文字小文字のフィールドcreateAtを追加します.
結果:
struct tagを使用する場合、いくつかの注意点があります. tagで識別する名前はjsonデータのkeyと呼ばれる値 である. tagは、このフィールド名の頭文字が大文字であっても、このフィールドがjsonデータに変換されないことを示すために jsonkeyの名前を文字"-"にするには、 を特殊に処理することができる.
tagに に変換されません.フィールドのタイプがbool、string、intクラス、floatクラスであり、tagに に変換される.
例:
jsonデータをstructに解析する(構造は既知)
jsonデータはstructまたは空のインタフェース
たとえば、次のjsonデータは次のとおりです.
このjsonデータを分析します.最上位の括弧は匿名のオブジェクトを表し、Goにマッピングされたstructは、このstruct名がPost であると仮定するの最上位カッコにはPost構造のフィールドがあります.これらのフィールドはjsonデータなので、頭文字を大文字にし、tagを設定する必要があります.tagの名前は と小文字にします. authorはサブオブジェクトであり、Goにマッピングされるのは別のstructであり、Postではこのフィールドの名前はAuthorであり、名前はstructの名前と同じであると仮定し、Author でもある. labelは配列であり、Goにマッピングされるのはsliceであってもarrayであってもよく、json arrayが空であるため、Goのslice/arrayタイプは不定であり、例えばintであってもよいし、stringであってもよいし、 であることが知られている. nextPostはサブオブジェクトであり、Goにマッピングされるのはstructであるが、jsonではこのオブジェクトがnullであり、このオブジェクトが存在しないことを示すため、Goにマッピングされるstructのタイプは判断できない.しかし、ここでの例では、次の記事はないので、そのタイプもPostタイプ であるべきである. commentはサブオブジェクトであり、配列に囲まれてGoにマッピングされ、slice/arrayであり、slice/arrayのタイプはstruct である.
解析後,structとstructのtagを対応的に構築することは容易である.以下に、上記の分析に基づいて構築されたデータ構造を示します.
なお、jsonデータの構築については前述したようにポインタが追跡されるので、ここで反転したstructでポインタタイプを使うのは問題ありません.
そこで,上記のjsonデータをPostタイプのオブジェクトに解析し,このjsonデータがa.jsonファイルに格納されていると仮定する.コードは次のとおりです.
出力結果:
jsonデータからstructがどれだけ複雑なのかを逆算するのは、論理的には難しくないが、データが複雑であれば、非常に気持ち悪いことだと感じているかもしれない.だから、他人が書いたツールを使って自動的に変換しましょう.この文書の後ろにはjsonからデータ構造への自動変換ツールが推奨されています.
解析jsonからinterface(構造不明)
上記は既知のjsonデータ構造の解析方式であり,json構造が未知であるか構造が変化する可能性がある場合structに解析するのは不合理である.このとき、空のインタフェース
例:
出力結果:
上にmap構造が出力されます.これは,タイプ対応関係で既に説明されているため,json objectがGo interfaceに解析した場合,map構造に対応する.上から出力された構造をフォーマットすると、次のような構造になります.
現在,このmapからタイプを判断し,対応する値を取得することができる.しかし、どのようにタイプを判断しますか?タイプブレークスルーを使用するには、次の手順に従います.
結果は次のとおりです.
Interfaceからの解析は非常に複雑であり,ネスト構造のために正確な反復遍歴ができない可能性があることが分かった.この場合、サードパーティ製パッケージsimplejsonを使用して、後述します.
jsonフローの解析、作成
jsonデータを直接解析したり作成したりするだけでなく、ストリームデータを処理したりすることもできます. type Decoder復号jsonからGoデータ構造 へ type Encoder符号化Goデータ構造からjson へ
例:
出力:
また、例えば、jsonデータを標準入力から読み、復号してNameという要素を削除し、最後に再符号化して標準出力に出力する.
json回転Goデータ構造ツール推奨
quicktypeツールは、jsonファイルを様々な言語に対応したデータ構造に簡単に変換できます.
アドレス:https://quicktype.io
vscodeに関連するプラグインがありますまずコマンドパネルに「set quicktype target language」と入力jsonをどの言語に変換するかを選択するデータ構造(Goなど) 「open quicktype for json」を入力すると、現在のjsonファイルをstructなどの対応するデータ構造 に変換できます.
変換後は、実際のニーズに合わせてタイプの一部を少し変更するだけです.例えばjsonトップクラスの匿名オブジェクトに対応するstructに名前を設定したり、structに変換できない場合にデータ型を判断するために使用される
たとえば、quicktypeツールを使用して、前の例のjsonデータを変換したデータ構造を次に示します.
そのうち
jsonデータフォーマットの説明を参照してください.
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データにカプセル化することができる.
例:
struct構造があります.
type Post struct {
Id int
Content string
Author string
}
この構造はブログ記事タイプを表しており,記事ID,記事内容,記事の提出者がある.これは何も言うことはありません.唯一示す必要があるのは、structであり、structはJSONデータにカプセル化(符号化)することができます.
このstructデータを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))
結果:
{"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"
}
structを除いてarray、slice、map構造はjsonに解析できますが、mapがjsonに解析される場合、keyはstringのみでなければなりません.これは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"
}
struct tagを使用してjsonの構築を支援
structが変換できるフィールドはすべて頭文字の大文字のフィールドですが、jsonで小文字の先頭のkeyを使用するにはstructのtagを使用して反射を補助することができます.
たとえば、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 tagを使用する場合、いくつかの注意点があります.
`json:"-"`
に設定することができる.`json:"-,"`
、すなわちカンマ,omitempty
のオプションがある場合、このフィールドの値が0の値、すなわちfalse、0、"、nilなどである場合、このフィールドはjsonの,string
オプションがある場合、このフィールドの値はjson文字列例:
type Post struct {
Id int `json:"ID,string"`
Content string `json:"content"`
Author string `json:"author"`
Label []string `json:"label,omitempty"`
}
jsonデータをstructに解析する(構造は既知)
jsonデータはstructまたは空のインタフェース
interface{}
に解析することができる(slice、mapなどであってもよい).上でjsonを構築する際のtag規則を理解すると,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データを分析します.
interface{}
であってもよい.ここでの例では、ラベルはstring 解析後,structとstructのtagを対応的に構築することは容易である.以下に、上記の分析に基づいて構築されたデータ構造を示します.
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データの構築については前述したようにポインタが追跡されるので、ここで反転したstructでポインタタイプを使うのは問題ありません.
そこで,上記のjsonデータをPostタイプのオブジェクトに解析し,このjsonデータがa.jsonファイルに格納されていると仮定する.コードは次のとおりです.
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 [] [0xc042072300 0xc0420723c0]}
jsonデータからstructがどれだけ複雑なのかを逆算するのは、論理的には難しくないが、データが複雑であれば、非常に気持ち悪いことだと感じているかもしれない.だから、他人が書いたツールを使って自動的に変換しましょう.この文書の後ろにはjsonからデータ構造への自動変換ツールが推奨されています.
解析jsonからinterface(構造不明)
上記は既知のjsonデータ構造の解析方式であり,json構造が未知であるか構造が変化する可能性がある場合structに解析するのは不合理である.このとき、空のインタフェース
interface{}
またはmap[string]interface{}
のタイプに解析することができ、この2つのタイプの結果は完全に一致する.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: 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:
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
value: ", vv)
fmt.Println("------------------")
case float64:
fmt.Println(k, "type: float64
value: ", vv)
fmt.Println("------------------")
case bool:
fmt.Println(k, "type: bool
value: ", vv)
fmt.Println("------------------")
case map[string]interface{}:
fmt.Println(k, "type: map[string]interface{}
value: ", vv)
for i, j := range vv {
fmt.Println(i,": ",j)
}
fmt.Println("------------------")
case []interface{}:
fmt.Println(k, "type: []interface{}
value: ", vv)
for key, value := range vv {
fmt.Println(key, ": ", value)
}
fmt.Println("------------------")
default:
fmt.Println(k, "type: nil
value: ", 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:
------------------
Interfaceからの解析は非常に複雑であり,ネスト構造のために正確な反復遍歴ができない可能性があることが分かった.この場合、サードパーティ製パッケージsimplejsonを使用して、後述します.
jsonフローの解析、作成
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
", 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)
}
}
}
json回転Goデータ構造ツール推奨
quicktypeツールは、jsonファイルを様々な言語に対応したデータ構造に簡単に変換できます.
アドレス:https://quicktype.io
vscodeに関連するプラグインがあります
変換後は、実際のニーズに合わせてタイプの一部を少し変更するだけです.例えばjsonトップクラスの匿名オブジェクトに対応するstructに名前を設定したり、structに変換できない場合にデータ型を判断するために使用される
interface{}
型も変更されます.たとえば、quicktypeツールを使用して、前の例のjsonデータを変換したデータ構造を次に示します.
type A struct {
ID int64 `json:"id"`
Content string `json:"content"`
Author Author `json:"author"`
Published bool `json:"published"`
Label []interface{} `json:"label"`
NextPost interface{} `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"`
}
そのうち
type A struct
のAを自分の名前に変更し、Aのinterface{}
も合理的なタイプに変更する必要があります.