コースタスク
-
構造データをjson文字ストリームにフォーマットするには、公式のencoding / jsonパッケージMarshal関数を参照してください。
エクスポートする必要があります
func JsonMarshal(v interface{}) ([]byte, error)
元のコードを参照したり、コピーしたりすることもできます
サポートフィールドタグ(タグ)、タグが一致
mytag:"你自己的定义"
サードパーティのパッケージは許可されていません
-
パッケージには次のものが含まれている必要があります。
生成された中国のapiドキュメント
簡単な使用例を含む、優れたReadmeファイルがあります
各goファイルには、対応するテストファイルが必要です
デザインの説明
関数を実装する新しいパッケージjson、新しいファイルjson.goを作成しますfunc JsonMarshal(v interface{}) ([]byte, error)
。
以前の調査では、go言語では、大文字のイニシャルは外部からアクセス可能であり、大文字でないイニシャルはアクセスできないことを示しています。でJsonMarshalの実現の機能、我々は必然的に他の関数を呼び出す必要が巣の人形に、我々は小文字で始める、つまり、他の不要な機能をカプセル化することができます。
公式のソースコードencoding / json / encode.goを通じて、リフレクトパッケージを使用して、マーシャル関数の一般的な実装プロセスを知ることができます。これは、後続の実装でも呼び出されます。
公式文書での実装は、多くの要因が考慮されているため比較的複雑であり、簡略化された使用可能なバージョンを実装した場合はそれほど考慮しませんでした。
1つ目は関数func JsonMarshal(v interface{}) ([]byte, error)
です。
func JsonMarshal(v interface{
}) ([]byte, error) {
b, err := marshal(v)
if err != nil {
return nil, err
}
return b, nil
}
これは、パッケージ内のプライベート関数marshalを呼び出すことによって実現されます。
次に、関数に移動しますfunc marshal(v interface{}) ([]byte, error)
。
func marshal(v interface{
}) ([]byte, error) {
if v == nil {
return []byte("null"), nil
}
s := reflect.ValueOf(v)
typeOfS := s.Type()
switch typeOfS.Kind() {
case reflect.Slice, reflect.Array :
return sliceMarshal(v)
case reflect.Struct :
return structMarshal(v)
case reflect.Map :
return mapMarshal(v)
case reflect.Ptr :
return marshal(s.Elem().Interface())
case reflect.String :
return []byte("\"" + s.String() + "\""), nil
default :
return []byte(fmt.Sprintf("%v", s.Interface())), nil
}
}
まず、型を決定する必要があります。直接返すことができるstringやintなどの基本的な型に加えて、Ptr型はアドレスを取得する必要があり、その他はすべて対応する関数の実装に渡す必要があります。
最初に見てくださいfunc sliceMarshal(v interface{}) ([]byte, error)
。
func sliceMarshal(v interface{
}) ([]byte, error) {
var b strings.Builder
b.WriteByte('[')
s := reflect.ValueOf(v)
for i := 0; i < s.Len(); i++ {
f := s.Index(i)
if i > 0 {
b.WriteByte(',')
}
tempB, e := marshal(f.Interface())
if e != nil {
return nil, e
}
b.Write(tempB)
}
b.WriteByte(']')
return []byte(b.String()), nil
}
アレイ/シャードを1回トラバースしてから、マーシャル関数を呼び出して、対応する値を再度解析します。マトリョシュカ
再びfunc structMarshal(v interface{}) ([]byte, error)
func structMarshal(v interface{
}) ([]byte, error) {
var b strings.Builder
b.WriteByte('{')
s := reflect.ValueOf(v)
typeOfS := s.Type()
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
if i > 0 {
b.WriteByte(',')
}
tag := typeOfS.Field(i).Tag.Get("mytag")
if tag == "" {
b.WriteString(fmt.Sprintf("\"%s\":", typeOfS.Field(i).Name))
} else {
b.WriteString(fmt.Sprintf("\"%s\":", tag))
}
tempB, e := marshal(f.Interface())
if e != nil {
return nil, e
}
b.Write(tempB)
}
b.WriteByte('}')
return []byte(b.String()), nil
}
構造型の解析は比較的複雑ですが、それほど多くはなく、変数名を出力するだけで済みます。タグがある場合は、対応するタグを印刷します。対応する値は、マーシャル関数を再度呼び出すことによっても解析されます。マトリョシュカ
最後はfunc mapMarshal(v interface{}) ([]byte, error)
です。
func mapMarshal(v interface{
}) ([]byte, error) {
var b strings.Builder
b.WriteByte('{')
s := reflect.ValueOf(v)
it := s.MapRange()
first := true
for it.Next() {
if first {
first = false
} else {
b.WriteByte(',')
}
b.WriteString(fmt.Sprintf("\"%v\":", it.Key()))
tempB, e := marshal(it.Value().Interface())
if e != nil {
return nil, e
}
b.Write(tempB)
}
b.WriteByte('}')
return []byte(b.String()), nil
}
それでも、マップをもう一度トラバースし、キーと値、および人形を印刷する必要があります
この時点で、機能func JsonMarshal(v interface{}) ([]byte, error)
は完了です。
テスト
各機能のテストを書くにjson.goすることは、それらのそれぞれが互いに人形を呼び出しますので、多くの使用をしていないようです。
type Monitor struct {
ID int
Name string
}
type Group struct {
ID int
Name string
Members []string
Nums [3]int
M Monitor `mytag:"GG"`
Manage map[int][]int
}
func ExampleSliceMarshal() {
b, err := sliceMarshal([]int{
1,2,3})
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(b))
//Output:[1,2,3]
}
func ExampleStructMarshal() {
group := Group{
ID:1,
Name:"Reds",
Members:[]string{
"Crimson", "Red", "Ruby", "Maroon"},
Nums:[3]int{
1,2,3},
M:Monitor{
18666,"yzdl"},
}
group.Manage= make(map[int][]int)
group.Manage[2] = []int{
1,2,3}
b, err := structMarshal(group)
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(b))
//Output:{"ID":1,"Name":"Reds","Members":["Crimson","Red","Ruby","Maroon"],"Nums":[1,2,3],"GG":{"ID":18666,"Name":"yzdl"},"Manage":{"2":[1,2,3]}}
}
func ExampleMapMarshal() {
M := make(map[int][]int)
M[2] = []int{
1,2,3}
b, err := mapMarshal(M)
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(b))
//Output:{"2":[1,2,3]}
}
func ExampleMarshal() {
b, err := marshal(123)
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(b))
//Output:123
}
func ExampleJsonMarshal() {
group := Group{
ID:1,
Name:"Reds",
Members:[]string{
"Crimson", "Red", "Ruby", "Maroon"},
Nums:[3]int{
1,2,3},
M:Monitor{
18666,"yzdl"},
}
group.Manage= make(map[int][]int)
group.Manage[2] = []int{
1,2,3}
b, err := JsonMarshal(&group)
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(b))
//Output:{"ID":1,"Name":"Reds","Members":["Crimson","Red","Ruby","Maroon"],"Nums":[1,2,3],"GG":{"ID":18666,"Name":"yzdl"},"Manage":{"2":[1,2,3]}}
}
基本的に、各タイプの値はマーシャルによって実行されます。
次に、命令を実行しますgo build github.com/github-user/HHH/json
。他のプログラムはimport "github.com/github-user/HHH/json"
、このパッケージの関数を呼び出すことができます。
test.goを作成して、試してみてください。
package main
import (
"fmt"
"github.com/github-user/HHH/json"
)
type Group struct {
ID int
Name string
}
type ColorGroup struct {
ID int
Name string
Colors []string
Nums [3]int
G Group `mytag:"GG"`
M map[int][]int
}
func main() {
group := ColorGroup{
ID:1,
Name:"Reds",
Colors:[]string{
"Crimson", "Red", "Ruby", "Maroon"},
Nums:[3]int{
1,2,3},
G:Group{
1,"123"},
}
group.M = make(map[int][]int)
group.M[2] = []int{
1,2,3}
b, err := json.JsonMarshal(group)
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(b))
b, err = json.JsonMarshal(&group)
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(b))
b, err = json.JsonMarshal("123456")
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(b))
}
命令が実行されgo run test.go
、プログラムは正常に実行されます。
APIドキュメントを生成する
前のジョブと同様に、実行godoc
は対応するパスを介してAPIにアクセスできます。