一个简洁、实用的GO的json库nGoJsons

258 阅读4分钟

一、概述

nGoJsons是一个集成方便应用层使用的Json库,是一个内部有内嵌的自研的Json库。

让应用层有更多的选择和方便使用。即有浅层的封装,也有自我特色的自研的Json库。

二、特点

  •     兼容官方 sdk的 Json接口    
  •     获取部分Json的解析数据

三、集成的开源库

3.1、序列化和反序列化支持的开源库

  • Stdlib
  • go-json
  • json-iterator
  • sonic  

3.2、Parse - Get 支持的开源库

  • fastjson
  • json-iterator
  • sonic
  • SimpleJson
  • jsonparser
  • ngeyjson

其中 ngeyjson是我自研的json库,目前只开放了Get接口部分。

四、统一的接口

4.1、序列化/反序列化的接口

type Decoder interface {
	Decode(val interface{}) error
	Buffered() io.Reader
	DisallowUnknownFields()
	More() bool
	UseNumber()
}

type Encoder interface {
	Encode(val interface{}) error
	SetEscapeHTML(on bool)
	SetIndent(prefix, indent string)
}

HTMLEscape
Marshal
MarshalIndent
Indent
Valid
Unmarshal
NewDecoder
NewEncoder

4.2、Parse - Get 接口

type IJsonParseRet interface {
	Get(key string) IJsonParseRet
	Array() ([]IJsonParseRet, error)

	Bool() (bool, error)

	String() (string, error)
	Float64() (float64, error)
	Int() (int, error)
	Uint() (uint, error)
	Int64() (int64, error)
	Uint64() (uint64, error)
}

五、用法

5.1、Marshal/Unmarshal  等

func testMarshal() {
	var s = struct {
		Name string
		Age  int
	}{
		"json",
		30,
	}

	fmt.Println("-----  Marshal Default Test -------")
	retbytes, err := nGoJsons.Marshal(&s)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(string(retbytes))

	fmt.Println("-----  Marshal StdlibJsonFrame Test -------")
	retbytes, err = nGoJsons.Marshal(&s, nGoJsons.SetJsonFrame(nGoJsons.StdlibJsonFrame))
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(string(retbytes))

	fmt.Println("-----  Marshal GoJsonFrame Test -------")
	retbytes, err = nGoJsons.Marshal(&s, nGoJsons.SetJsonFrame(nGoJsons.GoJsonFrame))
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(string(retbytes))

	fmt.Println("-----  Marshal SonicJsonFrame Test -------")
	retbytes, err = nGoJsons.Marshal(&s, nGoJsons.SetJsonFrame(nGoJsons.SonicJsonFrame))
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(string(retbytes))

	fmt.Println("-----  Marshal JsonIterJsonFrame Test -------")
	retbytes, err = nGoJsons.Marshal(&s, nGoJsons.SetJsonFrame(nGoJsons.JsonIterJsonFrame))
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(string(retbytes))
}

5.2、Get 用法

func TestJson1(t *testing.T) {
	s := []byte(`{"name":{"first":"Janet","last":"Prichard"},"age":47}`)

	var fn = func(v ijsoner.IJsonParseRet) {
		defer func() { JsonParse.ReleaseCache(v) }()

		vstr, err := v.String()
		if err != nil {
			t.Fatal(err)
		}
		t.Log(vstr)

		vstr, err = v.Get("name").String()
		if err != nil {
			t.Fatal(err)
		}
		t.Log(vstr)

		str, err := v.Get("name").Get("first").String()
		if err != nil {
			t.Fatal(err)
		}
		t.Log(str)

		str, err = v.Get("name").Get("last").String()
		if err != nil {
			t.Fatal(err)
		}
		t.Log(str)

		age, err := v.Get("age").Int()
		if err != nil {
			t.Fatal(err)
		}
		t.Log(age)
	}

	t.Run("testDefaultParse", func(t *testing.T) {
		v, err := JsonParse.Parse(s)
		if err != nil {
			t.Fatalf("unexpected error: %s", err)
		}
		fn(v)
	})

	t.Run("testSimpleJsonParse", func(t *testing.T) {
		v, err := JsonParse.Parse(s, JsonParse.SetParseFrame(JsonParse.SimpleJsonFrame))
		if err != nil {
			t.Fatalf("unexpected error: %s", err)
		}
		fn(v)
	})

	t.Run("testFastJsonParse", func(t *testing.T) {
		v, err := JsonParse.Parse(s, JsonParse.SetParseFrame(JsonParse.FastJsonFrame))
		if err != nil {
			t.Fatalf("unexpected error: %s", err)
		}
		fn(v)
	})

	t.Run("testGeyJsonParse", func(t *testing.T) {
		v, err := JsonParse.Parse(s, JsonParse.SetParseFrame(JsonParse.GeyJsonFrame))
		if err != nil {
			t.Fatalf("unexpected error: %s", err)
		}
		fn(v)
	})

	t.Run("testJsonIterParse", func(t *testing.T) {
		v, err := JsonParse.Parse(s, JsonParse.SetParseFrame(JsonParse.JsonIterFrame))
		if err != nil {
			t.Fatalf("unexpected error: %s", err)
		}
		fn(v)
	})

	t.Run("testJsonParser", func(t *testing.T) {
		v, err := JsonParse.Parse(s, JsonParse.SetParseFrame(JsonParse.JsonParserFrame))
		if err != nil {
			t.Fatalf("unexpected error: %s", err)
		}
		fn(v)
	})

	t.Run("testSonicJsonParse", func(t *testing.T) {
		v, err := JsonParse.Parse(s, JsonParse.SetParseFrame(JsonParse.SonicFrame))
		if err != nil {
			t.Fatalf("unexpected error: %s", err)
		}
		fn(v)
	})
}

六、 ngeyjson的设计

ngeyjson是一个立足于无反射的Json库。 
序列化和反序列化
type Marshaler interface { MarshalJSON() ([]byte, error) } 
type Unmarshaler interface { UnmarshalJSON([]byte) error } 

go sdk的 json 提供了对外接口。 
应用层可以实现 Marshaler 和 Unmarshaler , 达到无反射实现json的序列化和反序列化。
而对于复杂的Json, 一个是应用层处理比较麻烦,一个是不具有通用性。
可能这个项目序列反序列了特定结构,后续的项目可能还需要重新 实现。 

通过参考easyjson和ffjson的实现,我们将其进一步发展,完全可以实现这样一种方式: 
让程序来运行时自动生成序列和反序列的代码。这样的好处是: 
1) 如果反序列的结构有变化,可以自适应,具有扩展性。 
2) 不像 easyjson和ffjson那么麻烦,且具有侵入性。
3) 具有通用性,无论是自定义类型还是结构体类型都能完全适配.动态生成。 

这样的弊端是开始动态生成的代码比较耗时,但也仅仅是耗时一次,还有出问题可能调试跟踪定位比较麻烦。 
但随着这个框架能逐渐成熟的话,调试跟踪定位也会迎刃而解。 

解析部分Json 
这部分发展和参考了 fastjson的实现。但是没有做到sonic那么极致,还是在fastjson基础上的进一步完善优化。 
后续有时间可能考虑再进一步的高效的实现。 

相比较于 fastjson, ngeyjson的区别在于 
1、结构不同 
fastjson的Value结构:   
type Value struct { o Object a []*Value s string t Type   } 

ngeyjson的Value结构:   
type Value struct { o   Object a   []*Value spos int32 epos int32 t   Type }  

2、对 null 的支持 
ngeyjson对null支持大小写,大写和小写混合同样支持.比如 Null 

3、String的处理 
fastjson的String的处理是个败笔,作者在注释中也说仅仅让用在测试。 
ngeyjson对String的处理完全没有性能效率的问题. 

4、转义符的处理 
因为fastjson的String处理的不好,导致key对应的有转义符的字符串类型的值 并没有处理好。而ngeyjson完全支持。

5、后续迭代 
fastjson已经好久不更新了......

七、结束语

欢迎大家通过 github 来下载  github.com/bcwtlch/nGoJsons ,  通过进一步的使用来完善这个开源库。主要的出发点是方便大家来使用。github.com上有详细的说明使用和demo用例。

写这个开源库没用多久,测试和完善花费的时间更久。后续我还会开源一些有自己想法的方便使用的通用类的工具库。