Skip to content

Commit 5b0ec41

Browse files
committed
allow messages to provide their own validation impl
1 parent 97a81c9 commit 5b0ec41

File tree

2 files changed

+42
-0
lines changed

2 files changed

+42
-0
lines changed

jsonpb/jsonpb.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,6 +1107,13 @@ func (s mapKeys) Less(i, j int) bool {
11071107
// This function is used by both Marshal and Unmarshal. While required fields only exist in a
11081108
// proto2 message, a proto3 message can contain proto2 message(s).
11091109
func checkRequiredFields(pb proto.Message) error {
1110+
type validatingMessage interface {
1111+
ValidateRecursive() error
1112+
}
1113+
if vm, ok := pb.(validatingMessage); ok {
1114+
return vm.ValidateRecursive()
1115+
}
1116+
11101117
// Most well-known type messages do not contain required fields. The "Any" type may contain
11111118
// a message that has required fields.
11121119
//

jsonpb/jsonpb_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ package jsonpb
3434
import (
3535
"bytes"
3636
"encoding/json"
37+
"errors"
3738
"io"
3839
"math"
3940
"reflect"
@@ -533,6 +534,26 @@ func TestMarshalAnyJSONPBMarshaler(t *testing.T) {
533534
}
534535
}
535536

537+
func TestMarshalWithCustomValidation(t *testing.T) {
538+
msg := dynamicMessage{rawJson: `{ "foo": "bar", "baz": [0, 1, 2, 3] }`, dummy1: &dynamicMessage{}}
539+
540+
js, err := new(Marshaler).MarshalToString(&msg)
541+
if err != nil {
542+
t.Errorf("an unexpected error occurred when marshalling to json: %v", err)
543+
}
544+
err = Unmarshal(strings.NewReader(js), &msg)
545+
if err != nil {
546+
t.Errorf("an unexpected error occurred when unmarshalling from json: %v", err)
547+
}
548+
549+
// tickle an error in custom validation
550+
msg.dummy2 = int32(len(msg.rawJson) + 1)
551+
_, err = new(Marshaler).MarshalToString(&msg)
552+
if err == nil {
553+
t.Errorf("marshalling to json should have generated validation error but did not")
554+
}
555+
}
556+
536557
// Test marshaling message containing unset required fields should produce error.
537558
func TestMarshalUnsetRequiredFields(t *testing.T) {
538559
msgExt := &pb.Real{}
@@ -1004,6 +1025,13 @@ func (s *stringField) UnmarshalJSONPB(jum *Unmarshaler, js []byte) error {
10041025
// It provides implementations of JSONPBMarshaler and JSONPBUnmarshaler for JSON support.
10051026
type dynamicMessage struct {
10061027
rawJson string `protobuf:"bytes,1,opt,name=rawJson"`
1028+
1029+
// an unexported nested message is present just to ensure that it
1030+
// won't result in a panic (see issue #509)
1031+
dummy1 *dynamicMessage `protobuf:"bytes,2,opt,name=dummy1"`
1032+
1033+
// this is used to implement a custom validation rule
1034+
dummy2 int32 `protobuf:"varint,3,opt,name=dummy2"`
10071035
}
10081036

10091037
func (m *dynamicMessage) Reset() {
@@ -1026,6 +1054,13 @@ func (m *dynamicMessage) UnmarshalJSONPB(jum *Unmarshaler, js []byte) error {
10261054
return nil
10271055
}
10281056

1057+
func (m *dynamicMessage) ValidateRecursive() error {
1058+
if int(m.dummy2) > len(m.rawJson) {
1059+
return errors.New("dummy2 should be <= rawJson length")
1060+
}
1061+
return nil
1062+
}
1063+
10291064
// Test unmarshaling message containing unset required fields should produce error.
10301065
func TestUnmarshalUnsetRequiredFields(t *testing.T) {
10311066
tests := []struct {

0 commit comments

Comments
 (0)