diff --git a/lex/util/decoder.go b/lex/util/decoder.go index 38512a1f9..3c1054b1c 100644 --- a/lex/util/decoder.go +++ b/lex/util/decoder.go @@ -48,7 +48,7 @@ func JsonDecodeValue(b []byte) (any, error) { t, ok := lexTypesMap[tstr] if !ok { - return nil, fmt.Errorf("%w: %q", ErrUnrecognizedType, tstr) + t = reflect.TypeOf(UnknownType{}) } val := reflect.New(t) @@ -76,6 +76,10 @@ func CborDecodeValue(b []byte) (CBOR, error) { t, ok := lexTypesMap[tstr] if !ok { + // It is possible to gracefully handle this case, like in JsonDecodeValue, + // but since cbg.CBORUnmarshaller takes io.Reader as an input, + // that requires a function that would correctly advance the reader + // to the end of a value (and return the bytes). return nil, fmt.Errorf("%w: %q", ErrUnrecognizedType, tstr) } @@ -112,6 +116,12 @@ func (ltd *LexiconTypeDecoder) MarshalJSON() ([]byte, error) { if ltd == nil || ltd.Val == nil { return nil, fmt.Errorf("LexiconTypeDecoder MarshalJSON called on a nil") } + + if v, ok := ltd.Val.(*UnknownType); ok { + // Shortcut for passing through unrecognized values + return v.MarshalJSON() + } + v := reflect.ValueOf(ltd.Val) t := v.Type() sf, ok := t.Elem().FieldByName("LexiconTypeID") diff --git a/lex/util/decoder_test.go b/lex/util/decoder_test.go index dab5d6ba5..af414e986 100644 --- a/lex/util/decoder_test.go +++ b/lex/util/decoder_test.go @@ -1,6 +1,7 @@ package util import ( + "encoding/json" "testing" ) @@ -37,3 +38,14 @@ func TestNewFromType(t *testing.T) { t.Fatal("expect bogus generation to fail") } } + +func TestMissingTypeField(t *testing.T) { + var out struct { + Field *LexiconTypeDecoder + } + const input = `{"Field":{"Some_stuff":"but $type is missing"}}` + + if err := json.Unmarshal([]byte(input), &out); err != nil { + t.Fatalf("failed to unmarshal: %s", err) + } +} diff --git a/lex/util/util.go b/lex/util/util.go index b7396f38b..0ff4f2888 100644 --- a/lex/util/util.go +++ b/lex/util/util.go @@ -44,3 +44,19 @@ func CborTypeExtractReader(r io.Reader) (string, []byte, error) { return tcheck.Type, buf.Bytes(), nil } + +type UnknownType struct { + JSON json.RawMessage +} + +func (t *UnknownType) MarshalJSON() ([]byte, error) { + return t.JSON.MarshalJSON() +} + +func (t *UnknownType) UnmarshalJSON(b []byte) error { + return t.JSON.UnmarshalJSON(b) +} + +func (t *UnknownType) MarshalCBOR(w io.Writer) error { + return fmt.Errorf("converting unrecognized types from JSON to CBOR is not implemented") +}