This is an automated email from the ASF dual-hosted git repository.

wongoo pushed a commit to branch 1.6
in repository https://gitbox.apache.org/repos/asf/dubbo-go-hessian2.git


The following commit(s) were added to refs/heads/1.6 by this push:
     new f8682e1  merge for the release of v1.6.0 (#195)
f8682e1 is described below

commit f8682e1eb9f1965da066b01454a2e94d750711ba
Author: 望哥 <gelny...@163.com>
AuthorDate: Mon Jun 15 09:07:14 2020 +0800

    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 <dragonchar...@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: 望哥 <gelny...@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 <joe...@apache.org>
    Co-authored-by: Xin.Zh <dragonchar...@foxmail.com>
    Co-authored-by: huiren <zhr...@gmail.com>
    Co-authored-by: Huang YunKun <hty...@gmail.com>
    Co-authored-by: zonghaishang <y...@apache.org>
    Co-authored-by: fangyincheng <fangyc...@gmail.com>
    Co-authored-by: champly <cham...@outlook.com>
    Co-authored-by: wilson chen <willson.che...@gmail.com>
    Co-authored-by: fangyincheng <fangyinch...@sina.com>
    Co-authored-by: gaoxinge <gao...@gmail.com>
---
 .asf.yaml                                          |   5 +
 .gitignore                                         |   1 +
 .travis.yml                                        |  14 +-
 CHANGE.md                                          |  13 +
 LICENSE                                            |  25 ++
 README.md                                          |   7 +
 before_validate_license.sh                         |  26 ++
 binary_test.go                                     |   2 -
 decode.go                                          |  41 +++
 decode_test.go                                     |   1 +
 double.go                                          |  33 +++
 double_test.go                                     |  27 ++
 encode.go                                          |   2 +-
 go.mod                                             |   4 +-
 go.sum                                             |   6 +-
 int_test.go                                        |   5 +-
 object.go                                          |  87 +++++--
 object_test.go                                     | 109 ++++++++
 pojo.go                                            |   5 +
 request.go                                         |   5 +
 request_test.go                                    |  35 +++
 serialize.go                                       |   8 +-
 string.go                                          | 283 ++++++++++++++++++---
 string_test.go                                     |  37 +++
 .../src/main/java/test/TestCustomDecode.java       |   5 +
 .../src/main/java/test/TestCustomReply.java        |  28 ++
 test_hessian/src/main/java/test/TestString.java    |   5 +
 27 files changed, 739 insertions(+), 80 deletions(-)

diff --git a/.asf.yaml b/.asf.yaml
new file mode 100644
index 0000000..8d84e69
--- /dev/null
+++ b/.asf.yaml
@@ -0,0 +1,5 @@
+notifications:
+    commits:      commits@dubbo.apache.org
+    issues:       notificati...@dubbo.apache.org
+    pullrequests: notificati...@dubbo.apache.org
+    jira_options: link label link label
diff --git a/.gitignore b/.gitignore
index 1363720..fc62e8a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
 .idea
+vendor
 coverage.txt
diff --git a/.travis.yml b/.travis.yml
index a711aab..5442ab1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,6 @@
 language: go
 
-matrix:
+jobs:
   include:
     - language: java
       jdk: openjdk8
@@ -8,15 +8,23 @@ matrix:
 go:
   - "1.12"
 
+os: linux
+
+dist: xenial
+
 script:
   - mvn clean package -f test_hessian/pom.xml
   - mvn clean package -f test_dubbo/pom.xml
   - go fmt && [[ -z `git status -s` ]]
+  - sh before_validate_license.sh
+  - chmod u+x /tmp/tools/license/license-header-checker
+  - /tmp/tools/license/license-header-checker -v -a -r -i vendor  
/tmp/tools/license/license.txt . go  && [[ -z `git status -s` ]]
   - GO111MODULE=on && go mod vendor && go test -race -v && go test -bench . 
-race -coverprofile=coverage.txt
 
 after_success:
   - bash <(curl -s https://codecov.io/bash)
 
 notifications:
-  webhooks: 
https://oapi.dingtalk.com/robot/send?access_token=27a5eb4510c8cf913b67a72832549b123a8c44655483d20443515604669de0ae
-  webhooks: 
https://oapi.dingtalk.com/robot/send?access_token=8250008579ed1defda3a44fb8608a38d81a55700fdfb15466315a90a7dd2045f
+  webhooks:
+    - 
https://oapi.dingtalk.com/robot/send?access_token=27a5eb4510c8cf913b67a72832549b123a8c44655483d20443515604669de0ae
+    - 
https://oapi.dingtalk.com/robot/send?access_token=8250008579ed1defda3a44fb8608a38d81a55700fdfb15466315a90a7dd2045f
diff --git a/CHANGE.md b/CHANGE.md
index a3f32e4..492220c 100644
--- a/CHANGE.md
+++ b/CHANGE.md
@@ -1,5 +1,18 @@
 # Release Notes
 
+## v1.6.0
+
+### New Features
+- ignore non-exist fields when decoding. 
[#201](https://github.com/apache/dubbo-go-hessian2/pull/201)
+
+### Enhancement
+- add cache in reflection to improve performance. 
[#179](https://github.com/apache/dubbo-go-hessian2/pull/179)
+- string decode performance improvement. 
[#188](https://github.com/apache/dubbo-go-hessian2/pull/188)
+
+### Bugfixes
+- fix attachment lost for nil value. 
[#191](https://github.com/apache/dubbo-go-hessian2/pull/191)
+- fix float32 accuracy issue. 
[#196](https://github.com/apache/dubbo-go-hessian2/pull/196)
+
 ## v1.5.0
 
 ### New Features
diff --git a/LICENSE b/LICENSE
index 43bbf60..7dd1e2b 100755
--- a/LICENSE
+++ b/LICENSE
@@ -174,3 +174,28 @@
       of your accepting any such warranty or additional liability.
 
    END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/README.md b/README.md
index 9c12941..6bc9b78 100644
--- a/README.md
+++ b/README.md
@@ -8,8 +8,15 @@
 
 ---
 
+> **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](https://github.com/apache/dubbo-go).
 
+There is a big performance improvement, and some bugs fix for v1.6.0, 
+thanks to [micln](https://github.com/micln), 
[pantianying](https://github.com/pantianying), 
[zonghaishang](https://github.com/zonghaishang),
+ [willson-chen](https://github.com/willson-chen), 
[champly](https://github.com/champly).
+
 ## Feature List
 
 * [All JDK Exceptions](https://github.com/apache/dubbo-go-hessian2/issues/59)
diff --git a/before_validate_license.sh b/before_validate_license.sh
new file mode 100644
index 0000000..8fa6e38
--- /dev/null
+++ b/before_validate_license.sh
@@ -0,0 +1,26 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+remoteLicenseCheckerPath="https://github.com/dubbogo/resources/raw/master/tools/license";
+remoteLicenseCheckerName="license-header-checker"
+remoteLicenseCheckerURL="${remoteLicenseCheckerPath}/${remoteLicenseCheckerName}"
+remoteLicenseName="license.txt"
+remoteLicenseURL="${remoteLicenseCheckerPath}/${remoteLicenseName}"
+
+licensePath="/tmp/tools/license"
+mkdir -p ${licensePath}
+wget -P "${licensePath}" ${remoteLicenseCheckerURL}
+wget -P "${licensePath}" ${remoteLicenseURL}
diff --git a/binary_test.go b/binary_test.go
index dafda34..c145e13 100644
--- a/binary_test.go
+++ b/binary_test.go
@@ -20,8 +20,6 @@ package hessian
 import (
        "bytes"
        "fmt"
-
-       // "fmt"
        "testing"
 )
 
diff --git a/decode.go b/decode.go
index c5aafac..f10456c 100644
--- a/decode.go
+++ b/decode.go
@@ -50,15 +50,54 @@ func NewDecoder(b []byte) *Decoder {
        return &Decoder{reader: bufio.NewReader(bytes.NewReader(b)), typeRefs: 
&TypeRefs{records: map[string]bool{}}}
 }
 
+// NewDecoder generate a decoder instance
+func NewDecoderSize(b []byte, size int) *Decoder {
+       return &Decoder{reader: bufio.NewReaderSize(bytes.NewReader(b), size), 
typeRefs: &TypeRefs{records: map[string]bool{}}}
+}
+
 // NewDecoder generate a decoder instance with skip
 func NewDecoderWithSkip(b []byte) *Decoder {
        return &Decoder{reader: bufio.NewReader(bytes.NewReader(b)), typeRefs: 
&TypeRefs{records: map[string]bool{}}, isSkip: true}
 }
 
+// NewCheapDecoderWithSkip generate a decoder instance with skip,
+// only for cache pool, before decode Reset should be called.
+// For example, with pooling use, will effectively improve performance
+//
+//     var hessianPool = &sync.Pool{
+//             New: func() interface{} {
+//                     return hessian.NewCheapDecoderWithSkip([]byte{})
+//             },
+//     }
+//
+//     decoder := hessianPool.Get().(*hessian.Decoder)
+//     fill decode data
+//     decoder.Reset(data[:])
+//  decode anything ...
+//     hessianPool.Put(decoder)
+func NewCheapDecoderWithSkip(b []byte) *Decoder {
+       return &Decoder{reader: bufio.NewReader(bytes.NewReader(b)), isSkip: 
true}
+}
+
 /////////////////////////////////////////
 // utilities
 /////////////////////////////////////////
 
+func (d *Decoder) Reset(b []byte) *Decoder {
+       // reuse reader buf, avoid allocate
+       d.reader.Reset(bytes.NewReader(b))
+       d.typeRefs = &TypeRefs{records: map[string]bool{}}
+
+       if d.refs != nil {
+               d.refs = nil
+       }
+       if d.classInfoList != nil {
+               d.classInfoList = nil
+       }
+
+       return d
+}
+
 // peek a byte
 func (d *Decoder) peekByte() byte {
        return d.peek(1)[0]
@@ -150,6 +189,8 @@ func (d *Decoder) Decode() (interface{}, error) {
        return EnsureInterface(d.DecodeValue())
 }
 
+func (d *Decoder) Buffered() int { return d.reader.Buffered() }
+
 // DecodeValue parse hessian data, the return value maybe a reflection value 
when it's a map, list, object, or ref.
 func (d *Decoder) DecodeValue() (interface{}, error) {
        var (
diff --git a/decode_test.go b/decode_test.go
index f91aca0..3728fec 100644
--- a/decode_test.go
+++ b/decode_test.go
@@ -33,6 +33,7 @@ import (
 
 const (
        hessianJar = "test_hessian/target/test_hessian-1.0.0.jar"
+       testString = "hello, world! 你好,世界!"
 )
 
 func isFileExist(file string) bool {
diff --git a/double.go b/double.go
index d70c682..376a1f4 100644
--- a/double.go
+++ b/double.go
@@ -20,6 +20,7 @@ package hessian
 import (
        "encoding/binary"
        "math"
+       "strconv"
 )
 
 import (
@@ -62,6 +63,38 @@ END:
                byte(bits>>32), byte(bits>>24), byte(bits>>16), byte(bits>>8), 
byte(bits))
 }
 
+func encFloat32(b []byte, v float32) []byte {
+       fv := float32(int32(v))
+       if fv == v {
+               iv := int32(v)
+               switch iv {
+               case 0:
+                       return encByte(b, BC_DOUBLE_ZERO)
+               case 1:
+                       return encByte(b, BC_DOUBLE_ONE)
+               }
+               if iv >= -0x80 && iv < 0x80 {
+                       return encByte(b, BC_DOUBLE_BYTE, byte(iv))
+               } else if iv >= -0x8000 && iv < 0x8000 {
+                       return encByte(b, BC_DOUBLE_SHORT, byte(iv>>8), 
byte(iv))
+               }
+
+               goto END
+       }
+
+END:
+       if float32(int32(v*1000)) == v*1000 {
+               iv := int32(v * 1000)
+               return encByte(b, BC_DOUBLE_MILL, byte(iv>>24), byte(iv>>16), 
byte(iv>>8), byte(iv))
+       } else {
+               str := strconv.FormatFloat(float64(v), 'f', -1, 32)
+               d, _ := strconv.ParseFloat(str, 64)
+               bits := math.Float64bits(d)
+               return encByte(b, BC_DOUBLE, byte(bits>>56), byte(bits>>48), 
byte(bits>>40),
+                       byte(bits>>32), byte(bits>>24), byte(bits>>16), 
byte(bits>>8), byte(bits))
+       }
+}
+
 /////////////////////////////////////////
 // Double
 /////////////////////////////////////////
diff --git a/double_test.go b/double_test.go
index 111124b..43e9391 100644
--- a/double_test.go
+++ b/double_test.go
@@ -42,6 +42,33 @@ func TestEncDouble(t *testing.T) {
        t.Logf("decode(%v) = %v, %v\n", v, res, err)
 }
 
+func TestIssue181(t *testing.T) {
+       var (
+               v   float32
+               err error
+               e   *Encoder
+               d   *Decoder
+               res interface{}
+       )
+
+       e = NewEncoder()
+       v = 99.8
+       e.Encode(v)
+       if len(e.Buffer()) == 0 {
+               t.Fail()
+       }
+
+       // res would be '99.800003' without patches in PR #196
+       d = NewDecoder(e.Buffer())
+       res, err = d.Decode()
+       f := res.(float64)
+       if float32(f) != v {
+               t.Errorf("decode(%v) = %v, %v\n", v, res, err)
+               return
+       }
+       t.Logf("decode(%v) = %v\n", v, res)
+}
+
 func TestDouble(t *testing.T) {
        testDecodeFramework(t, "replyDouble_0_0", 0.0)
        testDecodeFramework(t, "replyDouble_0_001", 0.001)
diff --git a/encode.go b/encode.go
index fbeae99..ad9d5c6 100644
--- a/encode.go
+++ b/encode.go
@@ -112,7 +112,7 @@ func (e *Encoder) Encode(v interface{}) error {
                }
 
        case float32:
-               e.buffer = encFloat(e.buffer, float64(val))
+               e.buffer = encFloat32(e.buffer, val)
 
        case float64:
                e.buffer = encFloat(e.buffer, val)
diff --git a/go.mod b/go.mod
index 8497c83..fb0a269 100644
--- a/go.mod
+++ b/go.mod
@@ -1,7 +1,7 @@
 module github.com/apache/dubbo-go-hessian2
 
 require (
-       github.com/dubbogo/gost v1.5.1
-       github.com/pkg/errors v0.8.1
+       github.com/dubbogo/gost v1.9.0
+       github.com/pkg/errors v0.9.1
        github.com/stretchr/testify v1.4.0
 )
diff --git a/go.sum b/go.sum
index 6c93958..f799fe3 100644
--- a/go.sum
+++ b/go.sum
@@ -1,9 +1,11 @@
 github.com/davecgh/go-spew v1.1.0 
h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
 github.com/davecgh/go-spew v1.1.0/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dubbogo/gost v1.5.1 h1:oG5dzaWf1KYynBaBoUIOkgT+YD0niHV6xxI0Odq7hDg=
-github.com/dubbogo/gost v1.5.1/go.mod 
h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8=
+github.com/dubbogo/gost v1.9.0 h1:UT+dWwvLyJiDotxJERO75jB3Yxgsdy10KztR5ycxRAk=
+github.com/dubbogo/gost v1.9.0/go.mod 
h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8=
 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 github.com/pkg/errors v0.8.1/go.mod 
h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod 
h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 
h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod 
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/stretchr/objx v0.1.0/go.mod 
h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
diff --git a/int_test.go b/int_test.go
index 726a11f..082f399 100644
--- a/int_test.go
+++ b/int_test.go
@@ -18,10 +18,13 @@
 package hessian
 
 import (
-       "github.com/stretchr/testify/assert"
        "testing"
 )
 
+import (
+       "github.com/stretchr/testify/assert"
+)
+
 func TestEncInt32Len1B(t *testing.T) {
        var (
                v   int32
diff --git a/object.go b/object.go
index 4025dac..e2ef7b3 100644
--- a/object.go
+++ b/object.go
@@ -21,6 +21,7 @@ import (
        "io"
        "reflect"
        "strings"
+       "sync"
 )
 
 import (
@@ -164,21 +165,23 @@ func (e *Encoder) encObject(v POJO) error {
        structs := []reflect.Value{vv}
        for len(structs) > 0 {
                vv := structs[0]
+               vvt := vv.Type()
                num = vv.NumField()
                for i = 0; i < num; i++ {
+                       tf := vvt.Field(i)
                        // skip unexported anonymous field
-                       if vv.Type().Field(i).PkgPath != "" {
+                       if tf.PkgPath != "" {
                                continue
                        }
 
                        // skip ignored field
-                       if tag, _ := 
vv.Type().Field(i).Tag.Lookup(tagIdentifier); tag == `-` {
+                       if tag, _ := tf.Tag.Lookup(tagIdentifier); tag == `-` {
                                continue
                        }
 
                        field := vv.Field(i)
-                       if vv.Type().Field(i).Anonymous && field.Kind() == 
reflect.Struct {
-                               structs = append(structs, vv.Field(i))
+                       if tf.Anonymous && field.Kind() == reflect.Struct {
+                               structs = append(structs, field)
                                continue
                        }
 
@@ -292,36 +295,71 @@ func (d *Decoder) decClassDef() (interface{}, error) {
        return classInfo{javaName: clsName, fieldNameList: fieldList}, nil
 }
 
-func findField(name string, typ reflect.Type) ([]int, error) {
+type fieldInfo struct {
+       indexes []int
+       field   *reflect.StructField
+}
+
+// map[rType][fieldName]indexes
+var fieldIndexCache sync.Map
+
+func findFieldWithCache(name string, typ reflect.Type) ([]int, 
*reflect.StructField, error) {
+       typCache, _ := fieldIndexCache.Load(typ)
+       if typCache == nil {
+               typCache = &sync.Map{}
+               fieldIndexCache.Store(typ, typCache)
+       }
+
+       iindexes, existCache := typCache.(*sync.Map).Load(name)
+       if existCache && iindexes != nil {
+               finfo := iindexes.(*fieldInfo)
+               var err error
+               if len(finfo.indexes) == 0 {
+                       err = perrors.Errorf("failed to find field %s", name)
+               }
+               return finfo.indexes, finfo.field, err
+       }
+
+       indexes, field, err := findField(name, typ)
+       typCache.(*sync.Map).Store(name, &fieldInfo{indexes: indexes, field: 
field})
+       return indexes, field, err
+}
+
+// findField find structField in rType
+//
+// return
+//     indexes []int
+//     field reflect.StructField
+//     err error
+func findField(name string, typ reflect.Type) ([]int, *reflect.StructField, 
error) {
        for i := 0; i < typ.NumField(); i++ {
                // matching tag first, then lowerCamelCase, SameCase, lowerCase
 
                typField := typ.Field(i)
 
-               if val, has := typField.Tag.Lookup(tagIdentifier); has && 
strings.Compare(val, name) == 0 {
-                       return []int{i}, nil
-               }
+               tagVal, hasTag := typField.Tag.Lookup(tagIdentifier)
 
                fieldName := typField.Name
-               switch {
-               case strings.Compare(lowerCamelCase(fieldName), name) == 0:
-                       return []int{i}, nil
-               case strings.Compare(fieldName, name) == 0:
-                       return []int{i}, nil
-               case strings.Compare(strings.ToLower(fieldName), name) == 0:
-                       return []int{i}, nil
+               if hasTag && tagVal == name ||
+                       fieldName == name ||
+                       lowerCamelCase(fieldName) == name ||
+                       strings.ToLower(fieldName) == name {
+
+                       return []int{i}, &typField, nil
                }
 
                if typField.Anonymous && typField.Type.Kind() == reflect.Struct 
{
-                       next, _ := findField(name, typField.Type)
+                       next, field, _ := findField(name, typField.Type)
                        if len(next) > 0 {
-                               pos := []int{i}
-                               return append(pos, next...), nil
+                               indexes := []int{i}
+                               indexes = append(indexes, next...)
+
+                               return indexes, field, nil
                        }
                }
        }
 
-       return []int{}, perrors.Errorf("failed to find field %s", name)
+       return []int{}, nil, perrors.Errorf("failed to find field %s", name)
 }
 
 func (d *Decoder) decInstance(typ reflect.Type, cls classInfo) (interface{}, 
error) {
@@ -337,13 +375,14 @@ func (d *Decoder) decInstance(typ reflect.Type, cls 
classInfo) (interface{}, err
        for i := 0; i < len(cls.fieldNameList); i++ {
                fieldName := cls.fieldNameList[i]
 
-               index, err := findField(fieldName, typ)
+               index, fieldStruct, err := findFieldWithCache(fieldName, typ)
                if err != nil {
-                       return nil, perrors.Errorf("can not find field %s", 
fieldName)
+                       d.DecodeValue()
+                       continue
                }
 
                // skip unexported anonymous field
-               if vv.Type().FieldByIndex(index).PkgPath != "" {
+               if fieldStruct.PkgPath != "" {
                        continue
                }
 
@@ -357,8 +396,8 @@ func (d *Decoder) decInstance(typ reflect.Type, cls 
classInfo) (interface{}, err
 
                // unpack pointer to enable value setting
                fldRawValue := UnpackPtrValue(field)
-
                kind := fldTyp.Kind()
+
                switch kind {
                case reflect.String:
                        str, err := d.decString(TAG_READ)
@@ -483,7 +522,7 @@ func (d *Decoder) decInstance(typ reflect.Type, cls 
classInfo) (interface{}, err
                        }
 
                default:
-                       return nil, perrors.Errorf("unknown struct member type: 
%v %v", kind, typ.Name()+"."+typ.FieldByIndex(index).Name)
+                       return nil, perrors.Errorf("unknown struct member type: 
%v %v", kind, typ.Name()+"."+fieldStruct.Name)
                }
        } // end for
 
diff --git a/object_test.go b/object_test.go
index 2484d07..db66596 100644
--- a/object_test.go
+++ b/object_test.go
@@ -18,9 +18,15 @@
 package hessian
 
 import (
+       "encoding/json"
        "math"
        "reflect"
        "testing"
+       "time"
+)
+
+import (
+       "github.com/stretchr/testify/assert"
 )
 
 type Department struct {
@@ -659,3 +665,106 @@ func TestIssue150_EmbedStructJavaDecode(t *testing.T) {
 
        testJavaDecode(t, "customArgTypedFixed_Extends", dog)
 }
+
+type Mix struct {
+       A  int
+       B  string
+       CA time.Time
+       CB int64
+       CC string
+       CD []float64
+       D  map[string]interface{}
+}
+
+func (m Mix) JavaClassName() string {
+       return `test.mix`
+}
+
+func init() {
+       RegisterPOJO(new(Mix))
+}
+
+//
+// BenchmarkJsonEncode-8         217354              4799 ns/op             
832 B/op         15 allocs/op
+func BenchmarkJsonEncode(b *testing.B) {
+       m := Mix{A: int('a'), B: `hello`}
+       m.CD = []float64{1, 2, 3}
+       m.D = map[string]interface{}{`floats`: m.CD, `A`: m.A, `m`: m}
+
+       for i := 0; i < b.N; i++ {
+               _, err := json.Marshal(&m)
+               if err != nil {
+                       b.Error(err)
+               }
+       }
+}
+
+//
+// BenchmarkEncode-8             211452              5560 ns/op            
1771 B/op         51 allocs/op
+func BenchmarkEncode(b *testing.B) {
+       m := Mix{A: int('a'), B: `hello`}
+       m.CD = []float64{1, 2, 3}
+       m.D = map[string]interface{}{`floats`: m.CD, `A`: m.A, `m`: m}
+
+       for i := 0; i < b.N; i++ {
+               _, err := encodeTarget(&m)
+               if err != nil {
+                       b.Error(err)
+               }
+       }
+}
+
+//
+// BenchmarkJsonDecode-8         123922              8549 ns/op            
1776 B/op         51 allocs/op
+func BenchmarkJsonDecode(b *testing.B) {
+       m := Mix{A: int('a'), B: `hello`}
+       m.CD = []float64{1, 2, 3}
+       m.D = map[string]interface{}{`floats`: m.CD, `A`: m.A, `m`: m}
+       bytes, err := json.Marshal(&m)
+       if err != nil {
+               b.Error(err)
+       }
+
+       for i := 0; i < b.N; i++ {
+               m := &Mix{}
+               err := json.Unmarshal(bytes, m)
+               if err != nil {
+                       b.Error(err)
+               }
+       }
+}
+
+//
+// BenchmarkDecode-8             104196             10924 ns/op            
6424 B/op         98 allocs/op
+func BenchmarkDecode(b *testing.B) {
+       m := Mix{A: int('a'), B: `hello`}
+       m.CD = []float64{1, 2, 3}
+       m.D = map[string]interface{}{`floats`: m.CD, `A`: m.A, `m`: m}
+       bytes, err := encodeTarget(&m)
+       if err != nil {
+               b.Error(err)
+       }
+
+       for i := 0; i < b.N; i++ {
+               d := NewDecoder(bytes)
+               _, err := d.Decode()
+               if err != nil {
+                       b.Error(err)
+               }
+       }
+}
+
+type Person183 struct {
+       Name string
+}
+
+func (Person183) JavaClassName() string {
+       return `test.Person183`
+}
+
+func TestIssue183_DecodeExcessStructField(t *testing.T) {
+       RegisterPOJO(&Person183{})
+       got, err := decodeJavaResponse(`customReplyPerson183`, ``, false)
+       assert.NoError(t, err)
+       t.Logf("%T %+v", got, got)
+}
diff --git a/pojo.go b/pojo.go
index 0c9f9aa..be74bac 100644
--- a/pojo.go
+++ b/pojo.go
@@ -120,6 +120,11 @@ func RegisterPOJO(o POJO) int {
        pojoRegistry.Lock()
        defer pojoRegistry.Unlock()
 
+       if goName, ok := pojoRegistry.j2g[o.JavaClassName()]; ok {
+               return pojoRegistry.registry[goName].index
+       }
+
+       // JavaClassName shouldn't equal to goName
        if _, ok := pojoRegistry.registry[o.JavaClassName()]; ok {
                return -1
        }
diff --git a/request.go b/request.go
index 807f73f..020c789 100644
--- a/request.go
+++ b/request.go
@@ -337,6 +337,11 @@ func ToMapStringString(origin map[interface{}]interface{}) 
map[string]string {
        dest := make(map[string]string)
        for k, v := range origin {
                if kv, ok := k.(string); ok {
+                       if v == nil {
+                               dest[kv] = ""
+                               continue
+                       }
+
                        if vv, ok := v.(string); ok {
                                dest[kv] = vv
                        }
diff --git a/request_test.go b/request_test.go
index c64e013..b5886db 100644
--- a/request_test.go
+++ b/request_test.go
@@ -18,6 +18,7 @@
 package hessian
 
 import (
+       "reflect"
        "testing"
        "time"
 )
@@ -80,3 +81,37 @@ func TestDescRegex(t *testing.T) {
        assert.Equal(t, "[Ljava/lang/String;", results[0])
        assert.Equal(t, "[I", results[1])
 }
+
+func TestIssue192(t *testing.T) {
+       type args struct {
+               origin map[interface{}]interface{}
+       }
+       tests := []struct {
+               name string
+               args args
+               want map[string]string
+       }{
+               {
+                       name: "not null",
+                       args: args{
+                               origin: map[interface{}]interface{}{
+                                       "1": nil,
+                                       "2": "3",
+                                       "":  "",
+                               },
+                       },
+                       want: map[string]string{
+                               "1": "",
+                               "2": "3",
+                               "":  "",
+                       },
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       if got := ToMapStringString(tt.args.origin); 
!reflect.DeepEqual(got, tt.want) {
+                               t.Errorf("ToMapStringString() = %v, want %v", 
got, tt.want)
+                       }
+               })
+       }
+}
diff --git a/serialize.go b/serialize.go
index bb5860f..2584d10 100644
--- a/serialize.go
+++ b/serialize.go
@@ -42,12 +42,12 @@ type Serializer interface {
 
 var serializerMap = make(map[string]Serializer, 16)
 
-func SetSerializer(key string, codec Serializer) {
-       serializerMap[key] = codec
+func SetSerializer(javaClassName string, codec Serializer) {
+       serializerMap[javaClassName] = codec
 }
 
-func GetSerializer(key string) (Serializer, bool) {
-       codec, ok := serializerMap[key]
+func GetSerializer(javaClassName string) (Serializer, bool) {
+       codec, ok := serializerMap[javaClassName]
        return codec, ok
 }
 
diff --git a/string.go b/string.go
index f692438..a2a24ce 100644
--- a/string.go
+++ b/string.go
@@ -217,46 +217,46 @@ func encString(b []byte, v string) []byte {
 // ::= 'S' b1 b0 <utf8-data>         # string of length 0-65535
 // ::= [x00-x1f] <utf8-data>         # string of length 0-31
 // ::= [x30-x34] <utf8-data>         # string of length 0-1023
-func (d *Decoder) getStringLength(tag byte) (int32, error) {
-       var (
-               err    error
-               buf    [2]byte
-               length int32
-       )
+func (d *Decoder) getStringLength(tag byte) (int, error) {
+       var length int
 
        switch {
        case tag >= BC_STRING_DIRECT && tag <= STRING_DIRECT_MAX:
-               return int32(tag - 0x00), nil
+               return int(tag - 0x00), nil
 
        case tag >= 0x30 && tag <= 0x33:
-               _, err = io.ReadFull(d.reader, buf[:1])
+               b, err := d.readByte()
                if err != nil {
                        return -1, perrors.WithStack(err)
                }
 
-               length = int32(tag-0x30)<<8 + int32(buf[0])
+               length = int(tag-0x30)<<8 + int(b)
                return length, nil
 
        case tag == BC_STRING_CHUNK || tag == BC_STRING:
-               _, err = io.ReadFull(d.reader, buf[:2])
+               b0, err := d.readByte()
+               if err != nil {
+                       return -1, perrors.WithStack(err)
+               }
+
+               b1, err := d.readByte()
                if err != nil {
                        return -1, perrors.WithStack(err)
                }
-               length = int32(buf[0])<<8 + int32(buf[1])
+
+               length = int(b0)<<8 + int(b1)
                return length, nil
 
        default:
-               return -1, perrors.WithStack(err)
+               return -1, perrors.Errorf("string decode: unknown tag %b", tag)
        }
 }
 
 func (d *Decoder) decString(flag int32) (string, error) {
        var (
-               tag       byte
-               charTotal int32
-               last      bool
-               s         string
-               r         rune
+               tag  byte
+               last bool
+               s    string
        )
 
        if flag != TAG_READ {
@@ -311,24 +311,18 @@ func (d *Decoder) decString(flag int32) (string, error) {
                        last = true
                }
 
-               l, err := d.getStringLength(tag)
+               chunkLen, err := d.getStringLength(tag)
                if err != nil {
                        return s, perrors.WithStack(err)
                }
-               charTotal = l
-               charCount := 0
-
-               runeData := make([]rune, charTotal)
-               runeIndex := 0
-
-               byteCount := 0
-               byteLen := 0
-               charLen := 0
+               bytesBuf := make([]byte, chunkLen<<2)
+               offset := 0
 
                for {
-                       if int32(charCount) == charTotal {
+                       if chunkLen <= 0 {
                                if last {
-                                       return string(runeData[:runeIndex]), nil
+                                       b := bytesBuf[:offset]
+                                       return *(*string)(unsafe.Pointer(&b)), 
nil
                                }
 
                                b, _ := d.readByte()
@@ -343,21 +337,184 @@ func (d *Decoder) decString(flag int32) (string, error) {
                                                last = true
                                        }
 
-                                       l, err := d.getStringLength(b)
+                                       chunkLen, err = d.getStringLength(b)
                                        if err != nil {
                                                return s, perrors.WithStack(err)
                                        }
-                                       charTotal += l
-                                       bs := make([]rune, charTotal)
-                                       copy(bs, runeData)
-                                       runeData = bs
-
+                                       remain, cap := len(bytesBuf)-offset, 
chunkLen<<2
+                                       if remain < cap {
+                                               grow := len(bytesBuf) + cap
+                                               bs := make([]byte, grow)
+                                               copy(bs, bytesBuf)
+                                               bytesBuf = bs
+                                       }
                                default:
                                        return s, perrors.New("expect string 
tag")
                                }
                        }
 
-                       r, charLen, byteLen, err = decodeUcs4Rune(d.reader)
+                       if chunkLen > 0 {
+                               nread, err := d.next(bytesBuf[offset : 
offset+chunkLen])
+                               if err != nil {
+                                       if err == io.EOF {
+                                               break
+                                       }
+                                       return s, perrors.WithStack(err)
+                               }
+
+                               // quickly detect the actual number of bytes
+                               prev, i := offset, offset
+                               len := offset + nread
+                               copied := false
+                               for r, r1 := len-1, len-2; i < len; chunkLen-- {
+                                       ch := bytesBuf[offset]
+                                       if ch < 0x80 {
+                                               i++
+                                               offset++
+                                       } else if (ch & 0xe0) == 0xc0 {
+                                               i += 2
+                                               offset += 2
+                                       } else if (ch & 0xf0) == 0xe0 {
+                                               // handle the 3-byte right edge
+                                               // case:
+                                               // 1. Expect 3 bytes, but the 
current byte is on the right
+                                               // 2. Expect 3 bytes, but the 
current byte is second to last to the right
+                                               if i == r {
+                                                       bytesBuf[i+1], err = 
d.reader.ReadByte()
+                                                       if err != nil {
+                                                               return s, 
perrors.WithStack(err)
+                                                       }
+                                                       bytesBuf[i+2], err = 
d.reader.ReadByte()
+                                                       if err != nil {
+                                                               return s, 
perrors.WithStack(err)
+                                                       }
+                                                       nread += 2
+                                                       len += 2
+                                               } else if i == r1 {
+                                                       bytesBuf[i+2], err = 
d.reader.ReadByte()
+                                                       if err != nil {
+                                                               return s, 
perrors.WithStack(err)
+                                                       }
+                                                       nread++
+                                                       len++
+                                               }
+
+                                               // we detect emoji first
+                                               c1 := ((uint32(ch) & 0x0f) << 
12) + ((uint32(bytesBuf[i+1]) & 0x3f) << 6) + (uint32(bytesBuf[i+2]) & 0x3f)
+                                               if c1 >= 0xD800 && c1 <= 0xDBFF 
{
+
+                                                       var (
+                                                               c2  rune
+                                                               n2  int
+                                                               err error
+                                                               ch0 byte
+                                                       )
+
+                                                       // more cache byte 
available
+                                                       if i+3 < len {
+                                                               ch0 = 
bytesBuf[i+3]
+                                                       } else {
+                                                               ch0, err = 
d.reader.ReadByte()
+                                                               if err != nil {
+                                                                       return 
s, perrors.WithStack(err)
+                                                               }
+                                                               // update 
accumulates read bytes,
+                                                               // because it 
reads more than thunk bytes
+                                                               nread++
+                                                               len++
+                                                       }
+
+                                                       if ch0 < 0x80 {
+                                                               c2, n2 = 
rune(ch0), 1
+                                                       } else if (ch0 & 0xe0) 
== 0xc0 {
+                                                               var ch1 byte
+                                                               if i+4 < len {
+                                                                       ch1 = 
bytesBuf[i+4]
+                                                               } else {
+                                                                       // out 
of the chunk byte data
+                                                                       
bytesBuf[i+4], err = d.reader.ReadByte()
+                                                                       ch1 = 
bytesBuf[i+4]
+                                                                       nread++
+                                                                       len++
+                                                               }
+                                                               c2, n2 = 
rune(((uint32(ch0)&0x1f)<<6)+(uint32(ch1)&0x3f)), 2
+                                                       } else if (ch0 & 0xf0) 
== 0xe0 {
+                                                               var ch1, ch2 
byte
+                                                               if i+5 < len {
+                                                                       ch1 = 
bytesBuf[i+4]
+                                                                       ch2 = 
bytesBuf[i+5]
+                                                               } else {
+                                                                       ch1, 
err = d.reader.ReadByte()
+                                                                       if err 
!= nil {
+                                                                               
return s, perrors.WithStack(err)
+                                                                       }
+                                                                       ch2, 
err = d.reader.ReadByte()
+                                                                       len += 2
+                                                                       nread 
+= 2
+                                                               }
+                                                               c := 
((uint32(ch0) & 0x0f) << 12) + ((uint32(ch1) & 0x3f) << 6) + (uint32(ch2) & 
0x3f)
+                                                               c2, n2 = 
rune(c), 3
+                                                       }
+
+                                                       c := 
rune(c1-0xD800)<<10 + (c2 - 0xDC00) + 0x10000
+                                                       n3 := 
utf8.EncodeRune(bytesBuf[i:], c)
+                                                       if copied = n3 > 0 && 
n3 < /** front three byte */ 3+n2; copied {
+                                                               // We need to 
move the bytes,
+                                                               // for example, 
less bytes after decoding
+                                                               offset = i + n3
+                                                               
copy(bytesBuf[offset:], bytesBuf[i+3+n2:len])
+                                                       }
+
+                                                       i += n2
+                                                       chunkLen--
+                                               }
+                                               i += 3
+
+                                               // fix read the next byte index
+                                               if copied {
+                                                       copied = false
+                                                       continue
+                                               }
+
+                                               offset += 3
+                                       } else {
+                                               return s, perrors.Errorf("bad 
utf-8 encoding")
+                                       }
+                               }
+
+                               if remain := offset - prev - nread; remain > 0 {
+                                       if remain == 1 {
+                                               ch, err := d.readByte()
+                                               if err != nil {
+                                                       return s, 
perrors.WithStack(err)
+                                               }
+                                               bytesBuf[offset-1] = ch
+                                       } else {
+                                               var err error
+                                               if buffed := d.Buffered(); 
buffed < remain {
+                                                       // trigger fill data if 
required
+                                                       
copy(bytesBuf[offset-remain:offset], d.peek(remain))
+                                                       _, err = 
d.reader.Discard(remain)
+                                               } else {
+                                                       // copy remaining bytes.
+                                                       _, err = 
d.next(bytesBuf[offset-remain : offset])
+                                               }
+
+                                               if err != nil {
+                                                       return s, 
perrors.WithStack(err)
+                                               }
+                                       }
+                               }
+
+                               // the expected length string has been 
processed.
+                               if chunkLen <= 0 {
+                                       // we need to detect next chunk
+                                       continue
+                               }
+                       }
+
+                       // decode byte
+                       ch, err := d.readByte()
                        if err != nil {
                                if err == io.EOF {
                                        break
@@ -365,14 +522,58 @@ func (d *Decoder) decString(flag int32) (string, error) {
                                return s, perrors.WithStack(err)
                        }
 
-                       runeData[runeIndex] = r
-                       runeIndex++
+                       if ch < 0x80 {
+                               bytesBuf[offset] = ch
+                               offset++
+                       } else if (ch & 0xe0) == 0xc0 {
+                               ch1, err := d.readByte()
+                               if err != nil {
+                                       return s, perrors.WithStack(err)
+                               }
+                               bytesBuf[offset] = ch
+                               bytesBuf[offset+1] = ch1
+                               offset += 2
+                       } else if (ch & 0xf0) == 0xe0 {
+                               var err error
+                               if buffed := d.Buffered(); buffed < 2 {
+                                       // trigger fill data if required
+                                       copy(bytesBuf[offset+1:offset+3], 
d.peek(2))
+                                       _, err = d.reader.Discard(2)
+                               } else {
+                                       _, err = d.next(bytesBuf[offset+1 : 
offset+3])
+                               }
+                               if err != nil {
+                                       return s, perrors.WithStack(err)
+                               }
 
-                       charCount += charLen
-                       byteCount += byteLen
+                               bytesBuf[offset] = ch
+
+                               // we detect emoji first
+                               c1 := ((uint32(ch) & 0x0f) << 12) + 
((uint32(bytesBuf[offset+1]) & 0x3f) << 6) + (uint32(bytesBuf[offset+2]) & 0x3f)
+                               if c1 >= 0xD800 && c1 <= 0xDBFF {
+                                       c2, n2, err := decodeUcs2Rune(d.reader)
+                                       if err != nil {
+                                               return s, perrors.WithStack(err)
+                                       }
+
+                                       c := rune(c1-0xD800)<<10 + (c2 - 
0xDC00) + 0x10000
+                                       utf8.EncodeRune(bytesBuf[offset:], c)
+
+                                       // update next rune
+                                       offset += n2
+                                       chunkLen--
+                               }
+
+                               offset += 3
+                       } else {
+                               return s, perrors.Errorf("bad utf-8 encoding, 
offset=%d\n", offset)
+                       }
+
+                       chunkLen--
                }
 
-               return string(runeData[:runeIndex]), nil
+               b := bytesBuf[:offset]
+               return *(*string)(unsafe.Pointer(&b)), nil
        }
 
        return s, perrors.Errorf("unknown string tag %#x\n", tag)
diff --git a/string_test.go b/string_test.go
index 5751332..ae8d5c1 100644
--- a/string_test.go
+++ b/string_test.go
@@ -19,6 +19,7 @@ package hessian
 
 import (
        "fmt"
+       "sync"
        "testing"
 )
 
@@ -158,6 +159,34 @@ func TestStringEncode(t *testing.T) {
        testJavaDecode(t, "argString_65536", s65560[:65536])
 }
 
+var decodePool = &sync.Pool{
+       New: func() interface{} {
+               return NewCheapDecoderWithSkip([]byte{})
+       },
+}
+
+func TestStringWithPool(t *testing.T) {
+       e := NewEncoder()
+       e.Encode(testString)
+       buf := e.buffer
+
+       for i := 0; i < 3; i++ {
+               d := decodePool.Get().(*Decoder)
+               d.Reset(buf)
+
+               v, err := d.Decode()
+               if err != nil {
+                       t.Errorf("err:%s", err.Error())
+               }
+               if v != testString {
+                       t.Errorf("excpect decode %v, actual %v", testString, v)
+               }
+
+               decodePool.Put(d)
+       }
+
+}
+
 func TestStringEmoji(t *testing.T) {
        // see: test_hessian/src/main/java/test/TestString.java
        s0 := "emoji🤣"
@@ -166,3 +195,11 @@ func TestStringEmoji(t *testing.T) {
        testDecodeFramework(t, "customReplyStringEmoji", s0)
        testJavaDecode(t, "customArgString_emoji", s0)
 }
+
+func TestStringComplex(t *testing.T) {
+       // see: test_hessian/src/main/java/test/TestString.java
+       s0 := "킐\u0088中国你好!\u0088\u0088\u0088\u0088\u0088\u0088"
+
+       testDecodeFramework(t, "customReplyComplexString", s0)
+       testJavaDecode(t, "customArgComplexString", s0)
+}
diff --git a/test_hessian/src/main/java/test/TestCustomDecode.java 
b/test_hessian/src/main/java/test/TestCustomDecode.java
index e2394f5..1c4c276 100644
--- a/test_hessian/src/main/java/test/TestCustomDecode.java
+++ b/test_hessian/src/main/java/test/TestCustomDecode.java
@@ -200,6 +200,11 @@ public class TestCustomDecode {
         return TestString.getEmojiTestString().equals(o);
     }
 
+    public Object customArgComplexString() throws Exception {
+        String o = (String) input.readObject();
+        return TestString.getComplexString().equals(o);
+    }
+
     public Object customArgTypedFixedList_HashSet() throws Exception {
         HashSet o = (HashSet) input.readObject();
         return o.contains(0) && o.contains(1);
diff --git a/test_hessian/src/main/java/test/TestCustomReply.java 
b/test_hessian/src/main/java/test/TestCustomReply.java
index 813d7eb..12fc411 100644
--- a/test_hessian/src/main/java/test/TestCustomReply.java
+++ b/test_hessian/src/main/java/test/TestCustomReply.java
@@ -425,6 +425,23 @@ public class TestCustomReply {
         output.flush();
     }
 
+    public void customReplyPerson183() throws Exception {
+        Person183 p = new Person183();
+        p.name = "pname";
+        p.age = 13;
+        InnerPerson innerPerson = new InnerPerson();
+        innerPerson.name = "pname2";
+        innerPerson.age = 132;
+        p.innerPerson = innerPerson;
+        output.writeObject(p);
+        output.flush();
+    }
+
+    public void customReplyComplexString() throws Exception {
+        output.writeObject(TestString.getComplexString());
+        output.flush();
+    }
+
     public void customReplyExtendClass() throws Exception {
         Dog dog = new Dog();
         dog.name = "a dog";
@@ -486,3 +503,14 @@ class TypedListTest implements Serializable {
     }
 
 }
+
+class Person183 implements Serializable {
+    public String name;
+    public Integer age;
+    public InnerPerson innerPerson;
+}
+
+class InnerPerson implements Serializable {
+    public String name;
+    public Integer age;
+}
diff --git a/test_hessian/src/main/java/test/TestString.java 
b/test_hessian/src/main/java/test/TestString.java
index 2ffbdf3..c026796 100644
--- a/test_hessian/src/main/java/test/TestString.java
+++ b/test_hessian/src/main/java/test/TestString.java
@@ -29,4 +29,9 @@ public class TestString {
 
         return s + ",max" + maxUnicode;
     }
+
+    public static String getComplexString() {
+        String s = "킐\u0088中国你好!\u0088\u0088\u0088\u0088\u0088\u0088";
+        return s;
+    }
 }

Reply via email to