Skip to content

Commit

Permalink
Fix map outputting as array
Browse files Browse the repository at this point in the history
  • Loading branch information
dorner committed Nov 24, 2023
1 parent cd63a79 commit 1ee059e
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 34 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ protoc --avro_out=. --avro_opt=namespace_map=foo:bar,baz:spam *.proto
To Do List:

* Add tests
* Map is currently outputting as Array<Map> due to how Protobuf handles [maps](https://protobuf.com/docs/descriptors#map-fields) (as repeated entries). Need to fix.
* Need to decide on how to truly differentiate between optional and required fields (technically all fields are optional on Protobuf, but maybe we should use the actual `optional` keyword and only have those be optional in Avro?)
* Split up `main.go` - it's trying to do too much

Expand Down
10 changes: 10 additions & 0 deletions avro/array.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ func (t Array) ToJSON(types *TypeRepo) (any, error) {
if err != nil {
return nil, fmt.Errorf("error parsing item type: %w", err)
}
// you can't have a repeated map in protobuf, so if we are an array enclosing a map,
// we are *really* just a plain map. We'd see this code path because a map is encoded
// as a repeated "fake message type" that has key and value fields.
mapType, ok := itemJson.(*orderedmap.OrderedMap)
if ok {
returnedType, _ := mapType.Get("type")
if returnedType == "map" {
return itemJson, nil
}
}
jsonMap := orderedmap.New()
jsonMap.Set("type", "array")
jsonMap.Set("items", itemJson)
Expand Down
35 changes: 34 additions & 1 deletion avro/field.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func (t Field) ToJSON(types *TypeRepo) (any, error) {
if t.Default != "" {
jsonMap.Set("default", t.Default)
} else {
jsonMap.Set("default", DefaultValue(t.Type))
jsonMap.Set("default", DefaultValue(typeJson))
}
return jsonMap, nil
}
Expand Down Expand Up @@ -86,3 +86,36 @@ func BasicFieldTypeFromProto(proto *descriptorpb.FieldDescriptorProto) Type {
}
return Bare(proto.GetName())
}

func DefaultValue(t any) any {
switch t {
case "null":
return nil
case "boolean":
return false
case "int":
return 0
case "long":
return 0
case "float":
return 0.0
case "double":
return 0.0
case "map":
return map[string]any{}
case "record":
return map[string]any{}
case "array":
return []any{}
}

switch typedT := t.(type) {
case []any:
return DefaultValue(typedT[0])
case *orderedmap.OrderedMap:
val, _ := typedT.Get("type")
return DefaultValue(val)
}

return ""
}
6 changes: 6 additions & 0 deletions avro/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ func (t Map) ToJSON(types *TypeRepo) (any, error) {
if err != nil {
return nil, fmt.Errorf("error parsing map value type: %w", err)
}
// values are pretty much never a union of null and something else - but since Protobuf is
// optional by default, it seems like it is
mapType, ok := valueJson.([]any)
if ok && mapType[0] == "null"{
valueJson = mapType[1]
}
jsonMap := orderedmap.New()
jsonMap.Set("type", "map")
jsonMap.Set("values", valueJson)
Expand Down
32 changes: 0 additions & 32 deletions avro/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,35 +29,3 @@ func FullName(t NamedType) string {
return fmt.Sprintf(".%s.%s", t.GetNamespace(), t.GetName())
}

func DefaultValue(t Type) any {
switch t {
case Bare("null"):
return nil
case Bare("boolean"):
return false
case Bare("int"):
return 0
case Bare("long"):
return 0
case Bare("float"):
return 0.0
case Bare("double"):
return 0.0
}

switch typedT := t.(type) {
case Record:
return map[string]any{}
case Map:
return map[string]any{}
case Array:
return []string{}
case Union:
if typedT.Types[0] == Bare("null") {
return nil
}
return DefaultValue(typedT.Types[0])
}

return ""
}

0 comments on commit 1ee059e

Please sign in to comment.