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  }