merge for the release of v1.6.0 (#195)

* add license checker (#175)

* add release note for v1.5.0 (#178)

Co-authored-by: Xin.Zh <dragoncharlie@foxmail.com>

* Imp: cache in reflection (#179)

* benchmark result

* use cache in findField

* encode benchmark

* call field() once

* remove version

* fix import sync

* cache in registerPOJO

* add json bench result

* prune unneccessary rtype.Field(index)

* cache comment

* rename cache

* switch to if

* remove return value name

* findFieldWithCache

* remove if check when fieldStruct is nil

Co-authored-by: 望哥 <gelnyang@163.com>

* update dependency

* rename serialize arg name

* Create .asf.yaml

* 优化hessian解码string性能,提升54%

* optimize code.

* optimize code.

* fix code review.

* optimize codes.

* optimize cods.

* optimize code.

* update license

* go.sum

* ci go version

* testify -> 1.4.0

* testcase

* travis.yml

* decode value before reflect find

* setvalue

* decode nilPtr to nilPtr

* fix get attachment lost nil key

* manually import package

* add ToMapStringString unit test

* rename test function name with issue

* setmap

* support for decode emoji.

* refactor code

* add unit test.

* add unit tests.

* refactor tests.

* Update travis/main.sh (#200)

- Remove duplicate key 'webhooks'
- Key 'matrix' is an alias for `jobs`, using `jobs`
- Specify the os and dist explicitly

* Mod: modify

* Code format (#199)

* .gitignore

* code clean

* code clean

* remove length check

* Fix: comments

* Fix: format package

* Fix #181: float32 accuracy issue (#196)

* Fix #181: float32 accuracy issue

* Fix go fmt failure

* Add the unit test case for Issue181

* Add encFloat32 in double.go to encode float32 type

- Call encFloat32 to encode float32 while encoding
- Add unit test case to test float32 encoding

* Improve encFloat32 of double.go

* Fix git fmt failure

* add release note for v1.6.0 (#202)

* add release note for v1.5.1

* add release note for v1.5.1

* add notice

* update notice

* =fix release note for v1.6.0

Co-authored-by: Joe Zou <joezou@apache.org>
Co-authored-by: Xin.Zh <dragoncharlie@foxmail.com>
Co-authored-by: huiren <zhrlnt@gmail.com>
Co-authored-by: Huang YunKun <htynkn@gmail.com>
Co-authored-by: zonghaishang <yiji@apache.org>
Co-authored-by: fangyincheng <fangyc666@gmail.com>
Co-authored-by: champly <champly@outlook.com>
Co-authored-by: wilson chen <willson.chenwx@gmail.com>
Co-authored-by: fangyincheng <fangyincheng@sina.com>
Co-authored-by: gaoxinge <gaoxx5@gmail.com>
27 files changed
tree: 72f4eacb28e9a473b38e9956a6a38b8272d810ce
  1. .github/
  2. java_exception/
  3. test_dubbo/
  4. test_hessian/
  5. .asf.yaml
  6. .gitignore
  7. .golangci.yml
  8. .travis.yml
  9. before_validate_license.sh
  10. binary.go
  11. binary_test.go
  12. boolean.go
  13. boolean_test.go
  14. CHANGE.md
  15. codec.go
  16. codec_test.go
  17. const.go
  18. contributing.md
  19. date.go
  20. date_test.go
  21. decode.go
  22. decode_test.go
  23. doc.go
  24. double.go
  25. double_test.go
  26. encode.go
  27. encode_test.go
  28. go.mod
  29. go.sum
  30. hessian.go
  31. hessian_test.go
  32. int.go
  33. int_test.go
  34. java_collection.go
  35. java_collection_test.go
  36. java_exception.go
  37. java_exception_test.go
  38. LICENSE
  39. list.go
  40. list_test.go
  41. long.go
  42. long_test.go
  43. Makefile
  44. map.go
  45. map_test.go
  46. NOTICE
  47. null.go
  48. null_test.go
  49. object.go
  50. object_test.go
  51. pojo.go
  52. README.md
  53. ref.go
  54. ref_test.go
  55. request.go
  56. request_test.go
  57. response.go
  58. response_test.go
  59. serialize.go
  60. serialize_test.go
  61. string.go
  62. string_test.go
README.md

gohessian

Build Status codecov GoDoc Go Report Card license


Notice: When decoding, the java version of hessian will default skip and ignore non-exist fields. From the version of v1.6.0 , dubbo-go-hessian2 will skip non-exist fields too, while that before v1.6.0 will return errors.

It's a golang hessian library used by Apache/dubbo-go.

There is a big performance improvement, and some bugs fix for v1.6.0, thanks to micln, pantianying, zonghaishang, willson-chen, champly.

Feature List

hessian type mapping between Java and Go

Cross languages message definition should be careful, the following situations should be avoided:

  • define object that only exists in a special language
  • using various java exceptions (using error code/message instead)

So we can maintain a cross language type mapping:

hessian typejava typegolang type
nullnullnil
binarybyte[][]byte
booleanbooleanbool
datejava.util.Datetime.Time
doubledoublefloat64
intintint32
longlongint64
stringjava.lang.Stringstring
listjava.util.Listslice
mapjava.util.Mapmap
objectcustom define objectcustom define struct
OTHER COMMON USING TYPE
big decimaljava.math.BigDecimalgithub.com/dubbogo/gost/math/big/Decimal
big integerjava.math.BigIntegergithub.com/dubbogo/gost/math/big/Integer

reference

Basic Usage Examples

Encode To Bytes

type Circular struct {
	Value
	Previous *Circular
	Next     *Circular
}

type Value struct {
	Num int
}

func (Circular) JavaClassName() string {
	return "com.company.Circular"
}

c := &Circular{}
c.Num = 12345
c.Previous = c
c.Next = c

e := NewEncoder()
err := e.Encode(c)
if err != nil {
    panic(err)
}

bytes := e.Buffer()

Decode From Bytes

decodedObject, err := NewDecoder(bytes).Decode()
if err != nil {
    panic(err)
}
circular, ok := obj.(*Circular)
// ...

Customize Usage Examples

Encoding filed name

Hessian encoder default converts filed names of struct to lower camelcase, but you can customize it using hessian tag.

Example:

type MyUser struct {
	UserFullName      string   `hessian:"user_full_name"`
	FamilyPhoneNumber string   // default convert to => familyPhoneNumber
}

func (MyUser) JavaClassName() string {
	return "com.company.myuser"
}

user := &MyUser{
    UserFullName:      "username",
    FamilyPhoneNumber: "010-12345678",
}

e := hessian.NewEncoder()
err := e.Encode(user)
if err != nil {
    panic(err)
}

The encoded bytes of the struct MyUser is as following:

 00000000  43 12 63 6f 6d 2e 63 6f  6d 70 61 6e 79 2e 6d 79  |C.com.company.my|
 00000010  75 73 65 72 92 0e 75 73  65 72 5f 66 75 6c 6c 5f  |user..user_full_|
 00000020  6e 61 6d 65 11 66 61 6d  69 6c 79 50 68 6f 6e 65  |name.familyPhone|
 00000030  4e 75 6d 62 65 72 60 08  75 73 65 72 6e 61 6d 65  |Number`.username|
 00000040  0c 30 31 30 2d 31 32 33  34 35 36 37 38           |.010-12345678|

Decoding filed name

Hessian decoder finds the correct target field though comparing all filed names of struct one by one until matching.

The following example shows the order of the matching rules:

type MyUser struct {
	MobilePhone      string   `hessian:"mobile-phone"`
}

// You must define the tag of struct for lookup filed form encoded binary bytes, in this case:
// 00000000  43 12 63 6f 6d 2e 63 6f  6d 70 61 6e 79 2e 6d 79  |C.com.company.my|
// 00000010  75 73 65 72 91 0c 6d 6f  62 69 6c 65 2d 70 68 6f  |user..mobile-pho|
// 00000020  6e 65 60 0b 31 37 36 31  32 33 34 31 32 33 34     |ne`.17612341234|
//
// mobile-phone(tag lookup) => mobilePhone(lowerCameCase) => MobilePhone(SameCase) => mobilephone(lowercase)
// ^ will matched


type MyUser struct {
	MobilePhone      string
}

// The following encoded binary bytes will be hit automatically:
//
// 00000000  43 12 63 6f 6d 2e 63 6f  6d 70 61 6e 79 2e 6d 79  |C.com.company.my|
// 00000010  75 73 65 72 91 0b 6d 6f  62 69 6c 65 50 68 6f 6e  |user..mobilePhon|
// 00000020  65 60 0b 31 37 36 31 32  33 34 31 32 33 34        |e`.17612341234|
//
// mobile-phone(tag lookup) => mobilePhone(lowerCameCase) => MobilePhone(SameCase) => mobilephone(lowercase)
//                             ^ will matched
//
// 00000000  43 12 63 6f 6d 2e 63 6f  6d 70 61 6e 79 2e 6d 79  |C.com.company.my|
// 00000010  75 73 65 72 91 0b 4d 6f  62 69 6c 65 50 68 6f 6e  |user..MobilePhon|
// 00000020  65 60 0b 31 37 36 31 32  33 34 31 32 33 34        |e`.17612341234|
//
// mobile-phone(tag lookup) => mobilePhone(lowerCameCase) => MobilePhone(SameCase) => mobilephone(lowercase)
//                                                           ^ will matched
//
// 00000000  43 12 63 6f 6d 2e 63 6f  6d 70 61 6e 79 2e 6d 79  |C.com.company.my|
// 00000010  75 73 65 72 91 0b 6d 6f  62 69 6c 65 70 68 6f 6e  |user..mobilephon|
// 00000020  65 60 0b 31 37 36 31 32  33 34 31 32 33 34        |e`.17612341234|
//
// mobile-phone(tag lookup) => mobilePhone(lowerCameCase) => MobilePhone(SameCase) => mobilephone(lowercase)
//                                                                                    ^ will matched

hessian.SetTagIdentifier

You can use hessian.SetTagIdentifier to customize tag-identifier of hessian, which takes effect to both encoder and decoder.

Example:

hessian.SetTagIdentifier("json")

type MyUser struct {
	UserFullName      string   `json:"user_full_name"`
	FamilyPhoneNumber string   // default convert to => familyPhoneNumber
}

func (MyUser) JavaClassName() string {
	return "com.company.myuser"
}

user := &MyUser{
    UserFullName:      "username",
    FamilyPhoneNumber: "010-12345678",
}

e := hessian.NewEncoder()
err := e.Encode(user)
if err != nil {
    panic(err)
}

The encoded bytes of the struct MyUser is as following:

 00000000  43 12 63 6f 6d 2e 63 6f  6d 70 61 6e 79 2e 6d 79  |C.com.company.my|
 00000010  75 73 65 72 92 0e 75 73  65 72 5f 66 75 6c 6c 5f  |user..user_full_|
 00000020  6e 61 6d 65 11 66 61 6d  69 6c 79 50 68 6f 6e 65  |name.familyPhone|
 00000030  4e 75 6d 62 65 72 60 08  75 73 65 72 6e 61 6d 65  |Number`.username|
 00000040  0c 30 31 30 2d 31 32 33  34 35 36 37 38           |.010-12345678|

Using Java collections

By default, the output of Hessian Java impl of a Java collection like java.util.HashSet will be decoded as []interface{} in go-hessian2. To apply the one-to-one mapping relationship between certain Java collection class and your Go struct, examples are as follows:

//use HashSet as example
//define your struct, which should implements hessian.JavaCollectionObject
type JavaHashSet struct {
	value []interface{}
}

//get the inside slice value
func (j *JavaHashSet) Get() []interface{} {
	return j.value
}

//set the inside slice value
func (j *JavaHashSet) Set(v []interface{}) {
	j.value = v
}

//should be the same as the class name of the Java collection 
func (j *JavaHashSet) JavaClassName() string {
	return "java.util.HashSet"
}

func init() {
        //register your struct so that hessian can recognized it when encoding and decoding 
	SetCollectionSerialize(&JavaHashSet{})
}

Notice for inheritance

go-hessian2 supports inheritance struct, but the following situations should be avoided.

  • Avoid fields with the same name in multiple parent struct

The following struct C have inherited field Name(default from the first parent), but it's confused in logic.

type A struct { Name string }
type B struct { Name string }
type C struct {
	A
	B
}
  • Avoid inheritance for a pointer of struct

The following definition is valid for golang syntax, but the parent will be nil when create a new Dog, like dog := Dog{}, which will not happen in java inheritance, and is also not supported by go-hessian2.

type Dog struct {
	*Animal
}