@@ -121,6 +121,12 @@ func parsePrimitiveType(namespace, s string, cache *SchemaCache) (Schema, error)
121
121
122
122
func parseComplexType (namespace string , m map [string ]any , seen seenCache , cache * SchemaCache ) (Schema , error ) {
123
123
if val , ok := m ["type" ].([]any ); ok {
124
+ // Note: According to the spec, this is not allowed:
125
+ // https://avro.apache.org/docs/1.12.0/specification/#schema-declaration
126
+ // The "type" property in an object must be a string. A union type will be a slice,
127
+ // but NOT an object with a "type" property that is a slice.
128
+ // Might be advisable to remove this call (tradeoff between better conformance
129
+ // with the spec vs. possible backwards-compatibility issue).
124
130
return parseUnion (namespace , val , seen , cache )
125
131
}
126
132
@@ -131,10 +137,7 @@ func parseComplexType(namespace string, m map[string]any, seen seenCache, cache
131
137
typ := Type (str )
132
138
133
139
switch typ {
134
- case Null :
135
- return & NullSchema {}, nil
136
-
137
- case String , Bytes , Int , Long , Float , Double , Boolean :
140
+ case String , Bytes , Int , Long , Float , Double , Boolean , Null :
138
141
return parsePrimitive (typ , m )
139
142
140
143
case Record , Error :
@@ -158,14 +161,15 @@ func parseComplexType(namespace string, m map[string]any, seen seenCache, cache
158
161
}
159
162
160
163
type primitiveSchema struct {
161
- LogicalType string `mapstructure:"logicalType"`
162
- Precision int `mapstructure:"precision"`
163
- Scale int `mapstructure:"scale"`
164
- Props map [string ]any `mapstructure:",remain"`
164
+ Type string `mapstructure:"type"`
165
+ Props map [string ]any `mapstructure:",remain"`
165
166
}
166
167
167
168
func parsePrimitive (typ Type , m map [string ]any ) (Schema , error ) {
168
- if m == nil {
169
+ if len (m ) == 0 {
170
+ if typ == Null {
171
+ return & NullSchema {}, nil
172
+ }
169
173
return NewPrimitiveSchema (typ , nil ), nil
170
174
}
171
175
@@ -178,14 +182,20 @@ func parsePrimitive(typ Type, m map[string]any) (Schema, error) {
178
182
}
179
183
180
184
var logical LogicalSchema
181
- if p .LogicalType != "" {
182
- logical = parsePrimitiveLogicalType (typ , p .LogicalType , p .Precision , p .Scale )
185
+ if logicalType := logicalTypeProperty (p .Props ); logicalType != "" {
186
+ logical = parsePrimitiveLogicalType (typ , logicalType , p .Props )
187
+ if logical != nil {
188
+ delete (p .Props , "logicalType" )
189
+ }
183
190
}
184
191
192
+ if typ == Null {
193
+ return NewNullSchema (WithProps (p .Props )), nil
194
+ }
185
195
return NewPrimitiveSchema (typ , logical , WithProps (p .Props )), nil
186
196
}
187
197
188
- func parsePrimitiveLogicalType (typ Type , lt string , prec , scale int ) LogicalSchema {
198
+ func parsePrimitiveLogicalType (typ Type , lt string , props map [ string ] any ) LogicalSchema {
189
199
ltyp := LogicalType (lt )
190
200
if (typ == String && ltyp == UUID ) ||
191
201
(typ == Int && ltyp == Date ) ||
@@ -199,10 +209,10 @@ func parsePrimitiveLogicalType(typ Type, lt string, prec, scale int) LogicalSche
199
209
}
200
210
201
211
if typ == Bytes && ltyp == Decimal {
202
- return parseDecimalLogicalType (- 1 , prec , scale )
212
+ return parseDecimalLogicalType (- 1 , props )
203
213
}
204
214
205
- return nil
215
+ return nil // otherwise, not a recognized logical type
206
216
}
207
217
208
218
type recordSchema struct {
@@ -368,6 +378,7 @@ func parseEnum(namespace string, m map[string]any, seen seenCache, cache *Schema
368
378
}
369
379
370
380
type arraySchema struct {
381
+ Type string `mapstructure:"type"`
371
382
Items any `mapstructure:"items"`
372
383
Props map [string ]any `mapstructure:",remain"`
373
384
}
@@ -393,6 +404,7 @@ func parseArray(namespace string, m map[string]any, seen seenCache, cache *Schem
393
404
}
394
405
395
406
type mapSchema struct {
407
+ Type string `mapstructure:"type"`
396
408
Values any `mapstructure:"values"`
397
409
Props map [string ]any `mapstructure:",remain"`
398
410
}
@@ -431,15 +443,12 @@ func parseUnion(namespace string, v []any, seen seenCache, cache *SchemaCache) (
431
443
}
432
444
433
445
type fixedSchema struct {
434
- Name string `mapstructure:"name"`
435
- Namespace string `mapstructure:"namespace"`
436
- Aliases []string `mapstructure:"aliases"`
437
- Type string `mapstructure:"type"`
438
- Size int `mapstructure:"size"`
439
- LogicalType string `mapstructure:"logicalType"`
440
- Precision int `mapstructure:"precision"`
441
- Scale int `mapstructure:"scale"`
442
- Props map [string ]any `mapstructure:",remain"`
446
+ Name string `mapstructure:"name"`
447
+ Namespace string `mapstructure:"namespace"`
448
+ Aliases []string `mapstructure:"aliases"`
449
+ Type string `mapstructure:"type"`
450
+ Size int `mapstructure:"size"`
451
+ Props map [string ]any `mapstructure:",remain"`
443
452
}
444
453
445
454
func parseFixed (namespace string , m map [string ]any , seen seenCache , cache * SchemaCache ) (Schema , error ) {
@@ -463,8 +472,11 @@ func parseFixed(namespace string, m map[string]any, seen seenCache, cache *Schem
463
472
}
464
473
465
474
var logical LogicalSchema
466
- if f .LogicalType != "" {
467
- logical = parseFixedLogicalType (f .Size , f .LogicalType , f .Precision , f .Scale )
475
+ if logicalType := logicalTypeProperty (f .Props ); logicalType != "" {
476
+ logical = parseFixedLogicalType (f .Size , logicalType , f .Props )
477
+ if logical != nil {
478
+ delete (f .Props , "logicalType" )
479
+ }
468
480
}
469
481
470
482
fixed , err := NewFixedSchema (f .Name , f .Namespace , f .Size , logical , WithAliases (f .Aliases ), WithProps (f .Props ))
@@ -485,19 +497,41 @@ func parseFixed(namespace string, m map[string]any, seen seenCache, cache *Schem
485
497
return fixed , nil
486
498
}
487
499
488
- func parseFixedLogicalType (size int , lt string , prec , scale int ) LogicalSchema {
500
+ func parseFixedLogicalType (size int , lt string , props map [ string ] any ) LogicalSchema {
489
501
ltyp := LogicalType (lt )
490
502
switch {
491
503
case ltyp == Duration && size == 12 :
492
504
return NewPrimitiveLogicalSchema (Duration )
493
505
case ltyp == Decimal :
494
- return parseDecimalLogicalType (size , prec , scale )
506
+ return parseDecimalLogicalType (size , props )
495
507
}
496
508
497
509
return nil
498
510
}
499
511
500
- func parseDecimalLogicalType (size , prec , scale int ) LogicalSchema {
512
+ type decimalSchema struct {
513
+ Precision int `mapstructure:"precision"`
514
+ Scale int `mapstructure:"scale"`
515
+ }
516
+
517
+ func parseDecimalLogicalType (size int , props map [string ]any ) LogicalSchema {
518
+ var (
519
+ d decimalSchema
520
+ meta mapstructure.Metadata
521
+ )
522
+ if err := decodeMap (props , & d , & meta ); err != nil {
523
+ return nil
524
+ }
525
+ decType := newDecimalLogicalType (size , d .Precision , d .Scale )
526
+ if decType != nil {
527
+ // Remove the properties that we consumed
528
+ delete (props , "precision" )
529
+ delete (props , "scale" )
530
+ }
531
+ return decType
532
+ }
533
+
534
+ func newDecimalLogicalType (size , prec , scale int ) LogicalSchema {
501
535
if prec <= 0 {
502
536
return nil
503
537
}
@@ -594,3 +628,10 @@ func (c seenCache) Add(name string) error {
594
628
c [name ] = struct {}{}
595
629
return nil
596
630
}
631
+
632
+ func logicalTypeProperty (props map [string ]any ) string {
633
+ if lt , ok := props ["logicalType" ].(string ); ok {
634
+ return lt
635
+ }
636
+ return ""
637
+ }
0 commit comments