Copilot commented on code in PR #3175: URL: https://github.com/apache/dubbo-go/pull/3175#discussion_r2721393538
########## filter/generic/generalizer/bean.go: ########## @@ -0,0 +1,198 @@ +/* + * 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. + */ + +package generalizer + +import ( + "reflect" + "sync" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" +) + +import ( + "dubbo.apache.org/dubbo-go/v3/protocol/dubbo/hessian2" +) + +// JavaBeanDescriptor type constants +const ( + TypeClass = 1 + TypeEnum = 2 + TypeCollection = 3 + TypeMap = 4 + TypeArray = 5 + TypePrimitive = 6 + TypeBean = 7 +) + +// JavaBeanDescriptor corresponds to org.apache.dubbo.common.beanutil.JavaBeanDescriptor +type JavaBeanDescriptor struct { + ClassName string `json:"className"` + Type int `json:"type"` + Properties map[any]any `json:"properties"` +} + +// JavaClassName implements hessian.POJO +func (d *JavaBeanDescriptor) JavaClassName() string { + return "org.apache.dubbo.common.beanutil.JavaBeanDescriptor" +} + +func init() { + hessian.RegisterPOJO(&JavaBeanDescriptor{}) +} + +var ( + beanGeneralizer Generalizer + beanGeneralizerOnce sync.Once +) + +func GetBeanGeneralizer() Generalizer { + beanGeneralizerOnce.Do(func() { + beanGeneralizer = &BeanGeneralizer{} + }) + return beanGeneralizer +} + +// BeanGeneralizer converts objects to/from JavaBeanDescriptor format +type BeanGeneralizer struct{} + +func (g *BeanGeneralizer) Generalize(obj any) (any, error) { + if obj == nil { + return nil, nil + } + return g.toDescriptor(obj), nil +} + +func (g *BeanGeneralizer) Realize(obj any, typ reflect.Type) (any, error) { + if obj == nil { + return nil, nil + } + return GetMapGeneralizer().Realize(g.fromDescriptor(obj), typ) +} + +func (g *BeanGeneralizer) GetType(obj any) (string, error) { + return hessian2.GetJavaName(obj) +} + +// toDescriptor converts Go object to JavaBeanDescriptor +func (g *BeanGeneralizer) toDescriptor(obj any) *JavaBeanDescriptor { + if obj == nil { + return nil + } + + v := reflect.ValueOf(obj) + for v.Kind() == reflect.Ptr && !v.IsNil() { + v = v.Elem() + } + t := v.Type() + + className := "java.lang.Object" + if pojo, ok := obj.(hessian.POJO); ok { + className = pojo.JavaClassName() + } + + desc := &JavaBeanDescriptor{ClassName: className, Properties: make(map[any]any)} + + switch t.Kind() { + case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64, reflect.String: + desc.Type = TypePrimitive + desc.Properties["value"] = v.Interface() + + case reflect.Slice, reflect.Array: + desc.Type = TypeArray + for i := 0; i < v.Len(); i++ { + desc.Properties[i] = g.toDescriptor(v.Index(i).Interface()) + } + + case reflect.Map: + desc.Type = TypeMap + iter := v.MapRange() + for iter.Next() { + desc.Properties[g.toDescriptor(iter.Key().Interface())] = g.toDescriptor(iter.Value().Interface()) + } + + case reflect.Struct: + desc.Type = TypeBean + for i := 0; i < t.NumField(); i++ { + if fv := v.Field(i); fv.CanInterface() { + name := t.Field(i).Name + desc.Properties[string(name[0]|0x20)+name[1:]] = g.toDescriptor(fv.Interface()) + } + } + + default: + desc.Type = TypeBean + } + + return desc +} + +// fromDescriptor converts JavaBeanDescriptor to map for MapGeneralizer.Realize +func (g *BeanGeneralizer) fromDescriptor(obj any) any { + desc, ok := obj.(*JavaBeanDescriptor) + if !ok { + if m, ok := obj.(map[any]any); ok { + desc = &JavaBeanDescriptor{Properties: make(map[any]any)} + if v, ok := m["className"].(string); ok { + desc.ClassName = v + } + if v, ok := m["type"].(int32); ok { + desc.Type = int(v) + } + if v, ok := m["properties"].(map[any]any); ok { + desc.Properties = v + } + } else { + return obj + } + } + + switch desc.Type { + case TypePrimitive: + return desc.Properties["value"] + case TypeEnum: + return desc.Properties["name"] + case TypeArray, TypeCollection: + result := make([]any, len(desc.Properties)) + for i := 0; i < len(desc.Properties); i++ { + result[i] = g.fromDescriptor(desc.Properties[i]) + } Review Comment: `fromDescriptor` rebuilds arrays/collections by indexing `desc.Properties[i]` with `i` as `int`. When decoding a `JavaBeanDescriptor` from Java via Hessian, index keys are typically `int32` (Java `Integer`), so `desc.Properties[0]` lookups will miss and produce nil elements. Iterate `desc.Properties` and normalize key types (`int32`/`int64`/`int`) to indices when building the slice. ```suggestion // Normalize numeric keys (int, int32, int64) to indices when rebuilding arrays/collections. maxIndex := -1 for k := range desc.Properties { var idx int switch v := k.(type) { case int: idx = v case int32: idx = int(v) case int64: idx = int(v) default: continue } if idx > maxIndex { maxIndex = idx } } if maxIndex < 0 { return []any{} } result := make([]any, maxIndex+1) for k, v := range desc.Properties { var idx int switch key := k.(type) { case int: idx = key case int32: idx = int(key) case int64: idx = int(key) default: continue } if idx < 0 || idx >= len(result) { continue } result[idx] = g.fromDescriptor(v) } ``` ########## filter/generic/generalizer/bean.go: ########## @@ -0,0 +1,198 @@ +/* + * 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. + */ + +package generalizer + +import ( + "reflect" + "sync" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" +) + +import ( + "dubbo.apache.org/dubbo-go/v3/protocol/dubbo/hessian2" +) + +// JavaBeanDescriptor type constants +const ( + TypeClass = 1 + TypeEnum = 2 + TypeCollection = 3 + TypeMap = 4 + TypeArray = 5 + TypePrimitive = 6 + TypeBean = 7 +) + +// JavaBeanDescriptor corresponds to org.apache.dubbo.common.beanutil.JavaBeanDescriptor +type JavaBeanDescriptor struct { + ClassName string `json:"className"` + Type int `json:"type"` + Properties map[any]any `json:"properties"` +} + +// JavaClassName implements hessian.POJO +func (d *JavaBeanDescriptor) JavaClassName() string { + return "org.apache.dubbo.common.beanutil.JavaBeanDescriptor" +} + +func init() { + hessian.RegisterPOJO(&JavaBeanDescriptor{}) +} + +var ( + beanGeneralizer Generalizer + beanGeneralizerOnce sync.Once +) + +func GetBeanGeneralizer() Generalizer { + beanGeneralizerOnce.Do(func() { + beanGeneralizer = &BeanGeneralizer{} + }) + return beanGeneralizer +} + +// BeanGeneralizer converts objects to/from JavaBeanDescriptor format +type BeanGeneralizer struct{} + +func (g *BeanGeneralizer) Generalize(obj any) (any, error) { + if obj == nil { + return nil, nil + } + return g.toDescriptor(obj), nil +} + +func (g *BeanGeneralizer) Realize(obj any, typ reflect.Type) (any, error) { + if obj == nil { + return nil, nil + } + return GetMapGeneralizer().Realize(g.fromDescriptor(obj), typ) +} + +func (g *BeanGeneralizer) GetType(obj any) (string, error) { + return hessian2.GetJavaName(obj) +} + +// toDescriptor converts Go object to JavaBeanDescriptor +func (g *BeanGeneralizer) toDescriptor(obj any) *JavaBeanDescriptor { + if obj == nil { + return nil + } + + v := reflect.ValueOf(obj) + for v.Kind() == reflect.Ptr && !v.IsNil() { + v = v.Elem() + } + t := v.Type() Review Comment: `BeanGeneralizer.toDescriptor` treats a typed nil pointer (e.g. `(*T)(nil)` stored in an `any`) as a non-nil value and returns an empty `JavaBeanDescriptor` instead of `nil`. This will serialize nil pointer fields as a non-null bean on the wire. Add a `reflect.Ptr` (and optionally `reflect.Interface`) nil check (e.g. `v.Kind()==reflect.Ptr && v.IsNil()`) and return `nil` early. ########## filter/generic/generalizer/bean.go: ########## @@ -0,0 +1,198 @@ +/* + * 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. + */ + +package generalizer + +import ( + "reflect" + "sync" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" +) + +import ( + "dubbo.apache.org/dubbo-go/v3/protocol/dubbo/hessian2" +) + +// JavaBeanDescriptor type constants +const ( + TypeClass = 1 + TypeEnum = 2 + TypeCollection = 3 + TypeMap = 4 + TypeArray = 5 + TypePrimitive = 6 + TypeBean = 7 +) + +// JavaBeanDescriptor corresponds to org.apache.dubbo.common.beanutil.JavaBeanDescriptor +type JavaBeanDescriptor struct { + ClassName string `json:"className"` + Type int `json:"type"` + Properties map[any]any `json:"properties"` +} + +// JavaClassName implements hessian.POJO +func (d *JavaBeanDescriptor) JavaClassName() string { + return "org.apache.dubbo.common.beanutil.JavaBeanDescriptor" +} + +func init() { + hessian.RegisterPOJO(&JavaBeanDescriptor{}) +} + +var ( + beanGeneralizer Generalizer + beanGeneralizerOnce sync.Once +) + +func GetBeanGeneralizer() Generalizer { + beanGeneralizerOnce.Do(func() { + beanGeneralizer = &BeanGeneralizer{} + }) + return beanGeneralizer +} + +// BeanGeneralizer converts objects to/from JavaBeanDescriptor format +type BeanGeneralizer struct{} + +func (g *BeanGeneralizer) Generalize(obj any) (any, error) { + if obj == nil { + return nil, nil + } + return g.toDescriptor(obj), nil +} + +func (g *BeanGeneralizer) Realize(obj any, typ reflect.Type) (any, error) { + if obj == nil { + return nil, nil + } + return GetMapGeneralizer().Realize(g.fromDescriptor(obj), typ) +} + +func (g *BeanGeneralizer) GetType(obj any) (string, error) { + return hessian2.GetJavaName(obj) +} + +// toDescriptor converts Go object to JavaBeanDescriptor +func (g *BeanGeneralizer) toDescriptor(obj any) *JavaBeanDescriptor { + if obj == nil { + return nil + } + + v := reflect.ValueOf(obj) + for v.Kind() == reflect.Ptr && !v.IsNil() { + v = v.Elem() + } + t := v.Type() + + className := "java.lang.Object" + if pojo, ok := obj.(hessian.POJO); ok { + className = pojo.JavaClassName() + } + + desc := &JavaBeanDescriptor{ClassName: className, Properties: make(map[any]any)} + + switch t.Kind() { + case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64, reflect.String: + desc.Type = TypePrimitive + desc.Properties["value"] = v.Interface() + + case reflect.Slice, reflect.Array: + desc.Type = TypeArray + for i := 0; i < v.Len(); i++ { + desc.Properties[i] = g.toDescriptor(v.Index(i).Interface()) + } + + case reflect.Map: + desc.Type = TypeMap + iter := v.MapRange() + for iter.Next() { + desc.Properties[g.toDescriptor(iter.Key().Interface())] = g.toDescriptor(iter.Value().Interface()) + } + + case reflect.Struct: + desc.Type = TypeBean + for i := 0; i < t.NumField(); i++ { + if fv := v.Field(i); fv.CanInterface() { + name := t.Field(i).Name + desc.Properties[string(name[0]|0x20)+name[1:]] = g.toDescriptor(fv.Interface()) + } Review Comment: Property name generation `string(name[0]|0x20)+name[1:]` can panic for 1-character field names and is ASCII-only (incorrect for non-ASCII, and diverges from the existing `toUnexport` helper used in `map.go`). Consider reusing `toUnexport`/`setInMap` (including support for the `m` tag) or implementing Java’s bean decapitalize rules to avoid incorrect/mismatched property names. ########## filter/generic/generalizer/bean_test.go: ########## @@ -0,0 +1,206 @@ +/* + * 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. + */ + +package generalizer + +import ( + "reflect" + "testing" +) + +import ( + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestUser is a test struct for bean serialization +type TestUser struct { + Name string + Age int + Address *TestAddress +} + +func (t *TestUser) JavaClassName() string { + return "com.test.User" +} + +// TestAddress is a nested test struct +type TestAddress struct { + City string + ZipCode string +} + +func (t *TestAddress) JavaClassName() string { + return "com.test.Address" +} + +func TestBeanGeneralizer_Generalize_Primitive(t *testing.T) { + g := GetBeanGeneralizer() + + // Test string + result, err := g.Generalize("hello") + require.NoError(t, err) + desc := result.(*JavaBeanDescriptor) + assert.Equal(t, TypePrimitive, desc.Type) + assert.Equal(t, "hello", desc.Properties["value"]) + + // Test int + result, err = g.Generalize(42) + require.NoError(t, err) + desc = result.(*JavaBeanDescriptor) + assert.Equal(t, TypePrimitive, desc.Type) + assert.Equal(t, 42, desc.Properties["value"]) +} + +func TestBeanGeneralizer_Generalize_Nil(t *testing.T) { + g := GetBeanGeneralizer() + result, err := g.Generalize(nil) + require.NoError(t, err) + assert.Nil(t, result) +} Review Comment: The nil test only covers a literal `nil` interface. Add a test for a typed nil pointer (e.g. `var addr *TestAddress = nil; g.Generalize(addr)`) to ensure typed-nil pointers are treated as `nil` and not serialized as an empty `JavaBeanDescriptor`. ########## filter/generic/util_test.go: ########## @@ -77,7 +77,7 @@ func TestIsGeneric(t *testing.T) { assert.True(t, isGeneric("True")) assert.False(t, isGeneric("false")) assert.False(t, isGeneric("")) - assert.False(t, isGeneric("bean")) // 目前代码逻辑仅匹配 "true" + assert.True(t, isGeneric("bean")) // bean serialization is supported } Review Comment: `isGeneric` now recognizes more modes than just `true` (e.g. `gson`), but this test only asserts `true` and `bean`. Add an assertion for `isGeneric("gson")` (and optionally case variants) so the expanded behavior is covered. ########## filter/generic/generalizer/bean_test.go: ########## @@ -0,0 +1,206 @@ +/* + * 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. + */ + +package generalizer + +import ( + "reflect" + "testing" +) + +import ( + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestUser is a test struct for bean serialization +type TestUser struct { + Name string + Age int + Address *TestAddress +} + +func (t *TestUser) JavaClassName() string { + return "com.test.User" +} + +// TestAddress is a nested test struct +type TestAddress struct { + City string + ZipCode string +} + +func (t *TestAddress) JavaClassName() string { + return "com.test.Address" +} + +func TestBeanGeneralizer_Generalize_Primitive(t *testing.T) { + g := GetBeanGeneralizer() + + // Test string + result, err := g.Generalize("hello") + require.NoError(t, err) + desc := result.(*JavaBeanDescriptor) + assert.Equal(t, TypePrimitive, desc.Type) + assert.Equal(t, "hello", desc.Properties["value"]) + + // Test int + result, err = g.Generalize(42) + require.NoError(t, err) + desc = result.(*JavaBeanDescriptor) + assert.Equal(t, TypePrimitive, desc.Type) + assert.Equal(t, 42, desc.Properties["value"]) +} + +func TestBeanGeneralizer_Generalize_Nil(t *testing.T) { + g := GetBeanGeneralizer() + result, err := g.Generalize(nil) + require.NoError(t, err) + assert.Nil(t, result) +} + +func TestBeanGeneralizer_Generalize_Slice(t *testing.T) { + g := GetBeanGeneralizer() + + result, err := g.Generalize([]string{"a", "b", "c"}) + require.NoError(t, err) + desc := result.(*JavaBeanDescriptor) + assert.Equal(t, TypeArray, desc.Type) + assert.Len(t, desc.Properties, 3) +} + +func TestBeanGeneralizer_Generalize_Map(t *testing.T) { + g := GetBeanGeneralizer() + + result, err := g.Generalize(map[string]int{"one": 1, "two": 2}) + require.NoError(t, err) + desc := result.(*JavaBeanDescriptor) + assert.Equal(t, TypeMap, desc.Type) + assert.Len(t, desc.Properties, 2) +} + +func TestBeanGeneralizer_Generalize_Struct(t *testing.T) { + g := GetBeanGeneralizer() + + user := &TestUser{ + Name: "John", + Age: 30, + Address: &TestAddress{ + City: "Beijing", + ZipCode: "100000", + }, + } + + result, err := g.Generalize(user) + require.NoError(t, err) + desc := result.(*JavaBeanDescriptor) + assert.Equal(t, TypeBean, desc.Type) + assert.Equal(t, "com.test.User", desc.ClassName) +} + +func TestBeanGeneralizer_Realize_Primitive(t *testing.T) { + g := GetBeanGeneralizer() + + desc := &JavaBeanDescriptor{ + Type: TypePrimitive, + Properties: map[any]any{"value": "hello"}, + } + + result, err := g.Realize(desc, reflect.TypeOf("")) + require.NoError(t, err) + assert.Equal(t, "hello", result) +} + +func TestBeanGeneralizer_Realize_Nil(t *testing.T) { + g := GetBeanGeneralizer() + result, err := g.Realize(nil, reflect.TypeOf("")) + require.NoError(t, err) + assert.Nil(t, result) +} + +func TestBeanGeneralizer_RoundTrip_Struct(t *testing.T) { + g := GetBeanGeneralizer() + + original := &TestUser{ + Name: "Alice", + Age: 25, + Address: &TestAddress{ + City: "Shanghai", + ZipCode: "200000", + }, + } + + generalized, err := g.Generalize(original) + require.NoError(t, err) + + realized, err := g.Realize(generalized, reflect.TypeOf(&TestUser{})) + require.NoError(t, err) + + user := realized.(*TestUser) + assert.Equal(t, original.Name, user.Name) + assert.Equal(t, original.Age, user.Age) + assert.NotNil(t, user.Address) + assert.Equal(t, original.Address.City, user.Address.City) +} + +func TestBeanGeneralizer_RoundTrip_Slice(t *testing.T) { + g := GetBeanGeneralizer() + + original := []int{1, 2, 3, 4, 5} + + generalized, err := g.Generalize(original) + require.NoError(t, err) + Review Comment: Consider adding an interop-focused test that constructs a `JavaBeanDescriptor` for `TypeArray` with `Properties` keys as `int32` (matching Java `Integer` decoded by Hessian) and verifies `Realize` rebuilds the slice correctly. The current tests only round-trip Go-generated descriptors (which use `int` keys), so they won’t catch the Java->Go key-type mismatch. ########## filter/generic/generalizer/bean.go: ########## @@ -0,0 +1,198 @@ +/* + * 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. + */ + +package generalizer + +import ( + "reflect" + "sync" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" +) + +import ( + "dubbo.apache.org/dubbo-go/v3/protocol/dubbo/hessian2" +) + +// JavaBeanDescriptor type constants +const ( + TypeClass = 1 + TypeEnum = 2 + TypeCollection = 3 + TypeMap = 4 + TypeArray = 5 + TypePrimitive = 6 + TypeBean = 7 +) + +// JavaBeanDescriptor corresponds to org.apache.dubbo.common.beanutil.JavaBeanDescriptor +type JavaBeanDescriptor struct { + ClassName string `json:"className"` + Type int `json:"type"` + Properties map[any]any `json:"properties"` +} + +// JavaClassName implements hessian.POJO +func (d *JavaBeanDescriptor) JavaClassName() string { + return "org.apache.dubbo.common.beanutil.JavaBeanDescriptor" +} + +func init() { + hessian.RegisterPOJO(&JavaBeanDescriptor{}) +} + +var ( + beanGeneralizer Generalizer + beanGeneralizerOnce sync.Once +) + +func GetBeanGeneralizer() Generalizer { + beanGeneralizerOnce.Do(func() { + beanGeneralizer = &BeanGeneralizer{} + }) + return beanGeneralizer +} + +// BeanGeneralizer converts objects to/from JavaBeanDescriptor format +type BeanGeneralizer struct{} + +func (g *BeanGeneralizer) Generalize(obj any) (any, error) { + if obj == nil { + return nil, nil + } + return g.toDescriptor(obj), nil +} + +func (g *BeanGeneralizer) Realize(obj any, typ reflect.Type) (any, error) { + if obj == nil { + return nil, nil + } + return GetMapGeneralizer().Realize(g.fromDescriptor(obj), typ) +} + +func (g *BeanGeneralizer) GetType(obj any) (string, error) { + return hessian2.GetJavaName(obj) +} Review Comment: `BeanGeneralizer.GetType` directly returns `hessian2.GetJavaName(obj)` without the fallback behavior used by `MapGeneralizer.GetType`/`GsonGeneralizer.GetType` (e.g., defaulting to `java.lang.Object` on `NilError` or unknown types). This can leave the generic invocation `types` entry empty when args are `nil`/unrecognized, which can break method resolution on the Java side. Align `GetType` behavior with `MapGeneralizer.GetType` (including logging + safe defaults). -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
