github.com/confluentinc/confluent-kafka-go@v1.9.2/schemaregistry/serde/avro/avro_generic.go (about) 1 /** 2 * Copyright 2022 Confluent Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package avro 18 19 import ( 20 "reflect" 21 "unsafe" 22 23 "github.com/actgardner/gogen-avro/v10/parser" 24 "github.com/actgardner/gogen-avro/v10/schema" 25 "github.com/confluentinc/confluent-kafka-go/schemaregistry" 26 "github.com/confluentinc/confluent-kafka-go/schemaregistry/serde" 27 "github.com/heetch/avro" 28 ) 29 30 // GenericSerializer represents a generic Avro serializer 31 type GenericSerializer struct { 32 serde.BaseSerializer 33 } 34 35 // GenericDeserializer represents a generic Avro deserializer 36 type GenericDeserializer struct { 37 serde.BaseDeserializer 38 } 39 40 var _ serde.Serializer = new(GenericSerializer) 41 var _ serde.Deserializer = new(GenericDeserializer) 42 43 // NewGenericSerializer creates an Avro serializer for generic objects 44 func NewGenericSerializer(client schemaregistry.Client, serdeType serde.Type, conf *SerializerConfig) (*GenericSerializer, error) { 45 s := &GenericSerializer{} 46 err := s.ConfigureSerializer(client, serdeType, &conf.SerializerConfig) 47 if err != nil { 48 return nil, err 49 } 50 return s, nil 51 } 52 53 // Serialize implements serialization of generic Avro data 54 func (s *GenericSerializer) Serialize(topic string, msg interface{}) ([]byte, error) { 55 if msg == nil { 56 return nil, nil 57 } 58 val := reflect.ValueOf(msg) 59 if val.Kind() == reflect.Ptr { 60 // avro.TypeOf expects an interface containing a non-pointer 61 msg = val.Elem().Interface() 62 } 63 avroType, err := avro.TypeOf(msg) 64 if err != nil { 65 return nil, err 66 } 67 info := schemaregistry.SchemaInfo{ 68 Schema: avroType.String(), 69 } 70 id, err := s.GetID(topic, msg, info) 71 if err != nil { 72 return nil, err 73 } 74 msgBytes, _, err := avro.Marshal(msg) 75 if err != nil { 76 return nil, err 77 } 78 payload, err := s.WriteBytes(id, msgBytes) 79 if err != nil { 80 return nil, err 81 } 82 return payload, nil 83 } 84 85 // NewGenericDeserializer creates an Avro deserializer for generic objects 86 func NewGenericDeserializer(client schemaregistry.Client, serdeType serde.Type, conf *DeserializerConfig) (*GenericDeserializer, error) { 87 s := &GenericDeserializer{} 88 err := s.ConfigureDeserializer(client, serdeType, &conf.DeserializerConfig) 89 if err != nil { 90 return nil, err 91 } 92 return s, nil 93 } 94 95 // Deserialize implements deserialization of generic Avro data 96 func (s *GenericDeserializer) Deserialize(topic string, payload []byte) (interface{}, error) { 97 if payload == nil { 98 return nil, nil 99 } 100 info, err := s.GetSchema(topic, payload) 101 if err != nil { 102 return nil, err 103 } 104 writer, name, err := s.toType(info) 105 if err != nil { 106 return nil, err 107 } 108 subject, err := s.SubjectNameStrategy(topic, s.SerdeType, info) 109 if err != nil { 110 return nil, err 111 } 112 msg, err := s.MessageFactory(subject, name) 113 if err != nil { 114 return nil, err 115 } 116 _, err = avro.Unmarshal(payload[5:], msg, writer) 117 return msg, err 118 } 119 120 // DeserializeInto implements deserialization of generic Avro data to the given object 121 func (s *GenericDeserializer) DeserializeInto(topic string, payload []byte, msg interface{}) error { 122 if payload == nil { 123 return nil 124 } 125 info, err := s.GetSchema(topic, payload) 126 if err != nil { 127 return err 128 } 129 writer, _, err := s.toType(info) 130 _, err = avro.Unmarshal(payload[5:], msg, writer) 131 return err 132 } 133 134 func (s *GenericDeserializer) toType(schema schemaregistry.SchemaInfo) (*avro.Type, string, error) { 135 t := avro.Type{} 136 avroType, err := s.toAvroType(schema) 137 if err != nil { 138 return nil, "", err 139 } 140 141 // Use reflection to set the private avroType field of avro.Type 142 setPrivateAvroType(&t, avroType) 143 144 return &t, avroType.Name(), nil 145 } 146 147 func (s *GenericDeserializer) toAvroType(schema schemaregistry.SchemaInfo) (schema.AvroType, error) { 148 ns := parser.NewNamespace(false) 149 return resolveAvroReferences(s.Client, schema, ns) 150 } 151 152 // From https://stackoverflow.com/questions/42664837/how-to-access-unexported-struct-fields/43918797#43918797 153 func setPrivateAvroType(t *avro.Type, avroType schema.AvroType) { 154 rt := reflect.ValueOf(t).Elem() 155 rf := rt.Field(0) 156 reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())). 157 Elem(). 158 Set(reflect.ValueOf(avroType)) 159 }