サービスコンピューティング(5)-オブジェクトシリアル化サポートパッケージの開発

コースタスク

  1. 構造データをjson文字ストリームにフォーマットするには、公式のencoding / jsonパッケージMarshal関数を参照してください。

    エクスポートする必要があります func JsonMarshal(v interface{}) ([]byte, error)

    元のコードを参照したり、コピーしたりすることもできます

    サポートフィールドタグ(タグ)、タグが一致 mytag:"你自己的定义"

    サードパーティのパッケージは許可されていません

  2. パッケージには次のものが含まれている必要があります。

    生成された中国の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にアクセスできます。

ここに写真の説明を挿入

課題の提出

私の完全なコード

おすすめ

転載: blog.csdn.net/qq_43278234/article/details/109278731