diff --git a/codec_record.go b/codec_record.go index 4a01b12..d07a4aa 100644 --- a/codec_record.go +++ b/codec_record.go @@ -76,9 +76,17 @@ func decoderOfStruct(d *decoderContext, schema Schema, typ reflect2.Type) ValDec } } } - - // Skip field if it doesnt exist + // Skip field if it doesn't exist if sf == nil { + // If the field value doesn't exist in the binary, ignore it instead of + // appending a 'SkipDecoder'. + // + // Note: 'SkipDecoder' performs a read and moves the cursor, which, + // in this case, will lead to a dirty read. + if field.action == FieldSetDefault { + continue + } + fields = append(fields, &structFieldDecoder{ decoder: createSkipDecoder(field.Type()), }) diff --git a/schema_compatibility_test.go b/schema_compatibility_test.go index 83485cd..0b48254 100644 --- a/schema_compatibility_test.go +++ b/schema_compatibility_test.go @@ -976,3 +976,44 @@ func TestSchemaCompatibility_ResolveWithComplexUnion(t *testing.T) { want := map[string]any{"b": []byte("foo")} assert.Equal(t, want, result) } + +func TestSchemaCompatibility_ResolveWithFieldMissingInWriterAndReaderStruct(t *testing.T) { + w := avro.MustParse(`{ + "type":"record", "name":"test", "namespace": "org.hamba.avro", + "fields":[ + {"name": "a", "type": "string"} + ] + }`) + + r := avro.MustParse(`{ + "type":"record", "name":"test", "namespace": "org.hamba.avro", + "fields":[ + {"name": "a", "type": "string"}, + {"name": "b", "type": "int", "default": 10}, + {"name": "c", "type": "string", "default": "foo"} + ] + }`) + + type TestW struct { + A string `avro:"a"` + } + value := TestW{A: "abc"} + + b, err := avro.Marshal(w, value) + assert.NoError(t, err) + + schema, err := avro.NewSchemaCompatibility().Resolve(r, w) + assert.NoError(t, err) + + type TestR struct { + A string `avro:"a"` + C string `avro:"c"` + } + want := TestR{A: "abc", C: "foo"} + + var result TestR + err = avro.Unmarshal(schema, b, &result) + assert.NoError(t, err) + + assert.Equal(t, want, result) +}