Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support non-strict mode to decode object to map when unregistered #309

Merged
merged 7 commits into from
Jan 18, 2022

Conversation

wongoo
Copy link
Contributor

@wongoo wongoo commented Jan 18, 2022

What this PR does:

  • support non-strict mode to decode object to map when unregistered
  • support encode object to class

Which issue(s) this PR fixes:

Fixes #308

Special notes for your reviewer:

Does this PR introduce a user-facing change?:

- support non-strict mode to decode object to map when unregistered

example:

       name := UserName{
		FirstName: "John",
		LastName:  "Doe",
	}
	person := Person{
		UserName: name,
		Age:      18,
		Sex:      true,
	}

	worker1 := &Worker{
		Person: person,
		CurJob: JOB{Title: "cto", Company: "facebook"},
		Jobs: []JOB{
			{Title: "manager", Company: "google"},
			{Title: "ceo", Company: "microsoft"},
		},
	}

	t.Logf("worker1: %v", worker1)

	e := NewEncoder()
	err := e.Encode(worker1)
	if err != nil {
		t.Fatalf("encode(worker:%#v) = error:%s", worker1, err)
	}
	data := e.Buffer()
	t.Logf("data: %s", data)

	// unRegisterPOJO before decode, so that to decode to map
	unRegisterPOJO(name)
	unRegisterPOJO(person)
	unRegisterPOJO(worker1)
	unRegisterPOJO(&worker1.Jobs[0])

	// strict mode
	d := NewDecoder(data)
	d.Strict = true // set to strict mode, then the decoding will return error for class not being registered.
	res, err := d.Decode()
	if err == nil {
		t.Error("after unregister pojo, decoding should return error for strict mode")
		t.FailNow()
	}

	// non-strict mode, decode to map for class not being registered.
	d = NewDecoder(data)
	res, err = d.Decode()
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	t.Logf("type of decode object:%v", reflect.TypeOf(res))

	worker2, ok := res.(map[string]interface{}) // the response is a map
	if !ok {
		t.Fatalf("res:%#v should be a map for non-strict mode", res)
	}

	t.Logf("worker2: %v", worker2)

	// register pojo again, so that there are class definitions, which are used to encode map to object.
	RegisterPOJO(name)
	RegisterPOJO(person)
	RegisterPOJO(worker1)
	RegisterPOJO(&worker1.Jobs[0])

	// encode the map to object again, note the worker2 is a map.
	e = NewEncoder()
	err = e.Encode(worker2)
	if err != nil {
		t.Error(err)
		t.FailNow()
	}

	data = e.Buffer()
	t.Logf("data: %s", data)

	// decode the encoded map data to struct object again.
	d = NewDecoder(data)
	res, err = d.Decode()
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	t.Logf("type of decode object:%v", reflect.TypeOf(res))

	worker3, ok := res.(*Worker) // worker3 is a Worker
	if !ok {
		t.Fatalf("res:%#v should be a worker type", res)
	}

	t.Logf("worker3: %v", worker3)
	if !reflect.DeepEqual(worker1, worker3) {
		t.Fatal("worker1 not equal to worker3!")
	}

@codecov-commenter
Copy link

codecov-commenter commented Jan 18, 2022

Codecov Report

Merging #309 (f3f3a2e) into master (a80067e) will decrease coverage by 0.25%.
The diff coverage is 68.09%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #309      +/-   ##
==========================================
- Coverage   70.69%   70.44%   -0.26%     
==========================================
  Files          27       27              
  Lines        3027     3116      +89     
==========================================
+ Hits         2140     2195      +55     
- Misses        661      687      +26     
- Partials      226      234       +8     
Impacted Files Coverage Δ
java_collection.go 60.56% <0.00%> (ø)
response.go 54.18% <23.52%> (-1.21%) ⬇️
decode.go 68.08% <50.00%> (-1.39%) ⬇️
hessian.go 46.29% <57.14%> (-1.86%) ⬇️
object.go 65.67% <69.73%> (-0.61%) ⬇️
pojo.go 65.35% <81.08%> (+0.72%) ⬆️
encode.go 79.16% <100.00%> (+0.90%) ⬆️
int.go 81.48% <100.00%> (ø)
java_sql_time.go 64.17% <100.00%> (+3.06%) ⬆️
java_unknown_exception.go 85.71% <100.00%> (ø)
... and 5 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a80067e...f3f3a2e. Read the comment docs.

response.go Show resolved Hide resolved
@LaurenceLiZhixin
Copy link
Contributor

@wongoo
I have a question, can we encode a map without any registry? I think the map[string]interface{} contains all information about a pojo.
On the view of gateway filter, gateway can't get the real POJO of the object. But in the current implementation, this step is necessary.

// register pojo again, so that there are class definitions, which are used to encode map to object.
/**
Can these be omited.
**/
RegisterPOJO(name)
RegisterPOJO(person)
RegisterPOJO(worker1)
RegisterPOJO(&worker1.Jobs[0])

// encode the map to object again, note the worker2 is a map.
e = NewEncoder()
err = e.Encode(worker2)
if err != nil {
t.Error(err)
t.FailNow()
}

@wongoo
Copy link
Contributor Author

wongoo commented Jan 18, 2022

@LaurenceLiZhixin yes, we can encode a map directly, I will try to implement it.

decode.go Show resolved Hide resolved
@wongoo
Copy link
Contributor Author

wongoo commented Jan 18, 2022

@LaurenceLiZhixin I implement build class definition from map keys, and add a unit test:

func TestEncodeMapToObject(t *testing.T) {
	name := &UserName{
		FirstName: "John",
		LastName:  "Doe",
	}

	RegisterPOJO(name)

	// note: the first letter of the keys MUST lowercase.
	m := map[string]interface{}{
		"firstName": "John",
		"lastName":  "Doe",
	}

	e := NewEncoder()
	encErr := e.EncodeMapAsClass(name.JavaClassName(), m)
	if encErr != nil {
		t.Error(encErr)
		t.FailNow()
	}

	res := mustDecodeObject(t, e.Buffer())
	assert.True(t, reflect.DeepEqual(name, res))

	// note: the map contains the class key.
	m = map[string]interface{}{
		ClassKey:    name.JavaClassName(),
		"firstName": "John",
		"lastName":  "Doe",
	}

	// try to encode again
	e = NewEncoder()
	encErr = e.EncodeMapClass(m)
	if encErr != nil {
		t.Error(encErr)
		t.FailNow()
	}

	res = mustDecodeObject(t, e.Buffer())
	assert.True(t, reflect.DeepEqual(name, res))
}

@LaurenceLiZhixin
Copy link
Contributor

LaurenceLiZhixin commented Jan 18, 2022

I have verified this feature locally, and it works fine!
As Apache DUBBO PMC, Wongoo senior is very rigorous and efficient in dealing with new-coming issue, which is worth learning by us community members. On behalf of business workmates and aliRPC-Go's wide users, I would like to express my sincere thanks.
@wongoo

@LaurenceLiZhixin LaurenceLiZhixin merged commit ab8e186 into apache:master Jan 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

希望hessian库提供一个接口,可以将[]byte 和 map 结构互转。
6 participants