Skip to content

Instantly share code, notes, and snippets.

@jsmouret
Last active January 10, 2025 07:17
Show Gist options
  • Save jsmouret/2bc876e8def6c63410556350eca3e43d to your computer and use it in GitHub Desktop.
Save jsmouret/2bc876e8def6c63410556350eca3e43d to your computer and use it in GitHub Desktop.

Revisions

  1. jsmouret revised this gist Aug 3, 2017. 1 changed file with 99 additions and 73 deletions.
    172 changes: 99 additions & 73 deletions struct.go
    Original file line number Diff line number Diff line change
    @@ -1,14 +1,19 @@
    package pb

    import (
    "fmt"
    "reflect"

    st "github.com/golang/protobuf/ptypes/struct"
    )

    // ToStruct converts a map[string]interface{} to a ptypes.Struct
    func ToStruct(v map[string]interface{}) *st.Struct {
    fields := make(map[string]*st.Value, len(v))
    size := len(v)
    if size == 0 {
    return nil
    }
    fields := make(map[string]*st.Value, size)
    for k, v := range v {
    fields[k] = ToValue(v)
    }
    @@ -19,12 +24,14 @@ func ToStruct(v map[string]interface{}) *st.Struct {

    // ToValue converts an interface{} to a ptypes.Value
    func ToValue(v interface{}) *st.Value {

    // Try common types first
    switch v := v.(type) {
    case nil:
    return nil
    case bool:
    return &st.Value{
    Kind: &st.Value_NullValue{},
    Kind: &st.Value_BoolValue{
    BoolValue: v,
    },
    }
    case int:
    return &st.Value{
    @@ -92,97 +99,116 @@ func ToValue(v interface{}) *st.Value {
    StringValue: v,
    },
    }
    case bool:
    case error:
    return &st.Value{
    Kind: &st.Value_StringValue{
    StringValue: v.Error(),
    },
    }
    default:
    // Fallback to reflection for other types
    return toValue(reflect.ValueOf(v))
    }
    }

    func toValue(v reflect.Value) *st.Value {
    switch v.Kind() {
    case reflect.Bool:
    return &st.Value{
    Kind: &st.Value_BoolValue{
    BoolValue: v,
    BoolValue: v.Bool(),
    },
    }
    case error:
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    return &st.Value{
    Kind: &st.Value_StringValue{
    StringValue: v.Error(),
    Kind: &st.Value_NumberValue{
    NumberValue: float64(v.Int()),
    },
    }
    case map[string]string:
    fields := make(map[string]*st.Value, len(v))
    for k, v := range v {
    fields[k] = &st.Value{
    Kind: &st.Value_StringValue{
    StringValue: v,
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
    return &st.Value{
    Kind: &st.Value_NumberValue{
    NumberValue: float64(v.Uint()),
    },
    }
    case reflect.Float32, reflect.Float64:
    return &st.Value{
    Kind: &st.Value_NumberValue{
    NumberValue: v.Float(),
    },
    }
    case reflect.Ptr:
    if v.IsNil() {
    return nil
    }
    return toValue(reflect.Indirect(v))
    case reflect.Array, reflect.Slice:
    size := v.Len()
    if size == 0 {
    return nil
    }
    values := make([]*st.Value, size)
    for i := 0; i < size; i++ {
    values[i] = toValue(v.Index(i))
    }
    return &st.Value{
    Kind: &st.Value_ListValue{
    ListValue: &st.ListValue{
    Values: values,
    },
    },
    }
    case reflect.Struct:
    t := v.Type()
    size := v.NumField()
    if size == 0 {
    return nil
    }
    fields := make(map[string]*st.Value, size)
    for i := 0; i < size; i++ {
    name := t.Field(i).Name
    // Better way?
    if len(name) > 0 && 'A' <= name[0] && name[0] <= 'Z' {
    fields[name] = toValue(v.Field(i))
    }
    }
    if len(fields) == 0 {
    return nil
    }
    return &st.Value{
    Kind: &st.Value_StructValue{
    StructValue: &st.Struct{
    Fields: fields,
    },
    },
    }
    case map[string]interface{}:
    case reflect.Map:
    keys := v.MapKeys()
    if len(keys) == 0 {
    return nil
    }
    fields := make(map[string]*st.Value, len(keys))
    for _, k := range keys {
    if k.Kind() == reflect.String {
    fields[k.String()] = toValue(v.MapIndex(k))
    }
    }
    if len(fields) == 0 {
    return nil
    }
    return &st.Value{
    Kind: &st.Value_StructValue{
    StructValue: ToStruct(v),
    StructValue: &st.Struct{
    Fields: fields,
    },
    },
    }
    default:
    // Fallback to reflection for other types
    value := reflect.ValueOf(v)
    switch value.Kind() {
    case reflect.Array, reflect.Slice:
    size := value.Len()
    values := make([]*st.Value, size)
    for i := 0; i < size; i++ {
    item := value.Index(i).Interface()
    values[i] = ToValue(item)
    }
    return &st.Value{
    Kind: &st.Value_ListValue{
    ListValue: &st.ListValue{
    Values: values,
    },
    },
    }
    case reflect.Struct:
    t := value.Type()
    size := value.NumField()
    fields := make(map[string]*st.Value, size)
    for i := 0; i < size; i++ {
    name := t.Field(i).Name
    // Better way?
    if len(name) > 0 && 'A' <= name[0] && name[0] <= 'Z' {
    item := value.Field(i).Interface()
    fields[name] = ToValue(item)
    }
    }
    return &st.Value{
    Kind: &st.Value_StructValue{
    StructValue: &st.Struct{
    Fields: fields,
    },
    },
    }
    case reflect.Map:
    keys := value.MapKeys()
    fields := make(map[string]*st.Value, len(keys))
    for _, k := range keys {
    if k.Kind() == reflect.String {
    item := value.MapIndex(k).Interface()
    fields[k.String()] = ToValue(item)
    }
    }
    return &st.Value{
    Kind: &st.Value_StructValue{
    StructValue: &st.Struct{
    Fields: fields,
    },
    },
    }
    default:
    return &st.Value{
    Kind: &st.Value_NullValue{},
    }
    // Last resort
    return &st.Value{
    Kind: &st.Value_StringValue{
    StringValue: fmt.Sprint(v),
    },
    }
    }
    }
  2. jsmouret revised this gist Aug 3, 2017. 1 changed file with 6 additions and 3 deletions.
    9 changes: 6 additions & 3 deletions struct.go
    Original file line number Diff line number Diff line change
    @@ -134,7 +134,8 @@ func ToValue(v interface{}) *st.Value {
    size := value.Len()
    values := make([]*st.Value, size)
    for i := 0; i < size; i++ {
    values[i] = ToValue(value.Index(i))
    item := value.Index(i).Interface()
    values[i] = ToValue(item)
    }
    return &st.Value{
    Kind: &st.Value_ListValue{
    @@ -151,7 +152,8 @@ func ToValue(v interface{}) *st.Value {
    name := t.Field(i).Name
    // Better way?
    if len(name) > 0 && 'A' <= name[0] && name[0] <= 'Z' {
    fields[name] = ToValue(value.Field(i))
    item := value.Field(i).Interface()
    fields[name] = ToValue(item)
    }
    }
    return &st.Value{
    @@ -166,7 +168,8 @@ func ToValue(v interface{}) *st.Value {
    fields := make(map[string]*st.Value, len(keys))
    for _, k := range keys {
    if k.Kind() == reflect.String {
    fields[k.String()] = ToValue(value.MapIndex(k))
    item := value.MapIndex(k).Interface()
    fields[k.String()] = ToValue(item)
    }
    }
    return &st.Value{
  3. jsmouret revised this gist Aug 3, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion struct.go
    Original file line number Diff line number Diff line change
    @@ -151,7 +151,7 @@ func ToValue(v interface{}) *st.Value {
    name := t.Field(i).Name
    // Better way?
    if len(name) > 0 && 'A' <= name[0] && name[0] <= 'Z' {
    fields[t.Field(i).Name] = ToValue(value.Field(i))
    fields[name] = ToValue(value.Field(i))
    }
    }
    return &st.Value{
  4. jsmouret created this gist Aug 3, 2017.
    185 changes: 185 additions & 0 deletions struct.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,185 @@
    package pb

    import (
    "reflect"

    st "github.com/golang/protobuf/ptypes/struct"
    )

    // ToStruct converts a map[string]interface{} to a ptypes.Struct
    func ToStruct(v map[string]interface{}) *st.Struct {
    fields := make(map[string]*st.Value, len(v))
    for k, v := range v {
    fields[k] = ToValue(v)
    }
    return &st.Struct{
    Fields: fields,
    }
    }

    // ToValue converts an interface{} to a ptypes.Value
    func ToValue(v interface{}) *st.Value {

    // Try common types first
    switch v := v.(type) {
    case nil:
    return &st.Value{
    Kind: &st.Value_NullValue{},
    }
    case int:
    return &st.Value{
    Kind: &st.Value_NumberValue{
    NumberValue: float64(v),
    },
    }
    case int8:
    return &st.Value{
    Kind: &st.Value_NumberValue{
    NumberValue: float64(v),
    },
    }
    case int32:
    return &st.Value{
    Kind: &st.Value_NumberValue{
    NumberValue: float64(v),
    },
    }
    case int64:
    return &st.Value{
    Kind: &st.Value_NumberValue{
    NumberValue: float64(v),
    },
    }
    case uint:
    return &st.Value{
    Kind: &st.Value_NumberValue{
    NumberValue: float64(v),
    },
    }
    case uint8:
    return &st.Value{
    Kind: &st.Value_NumberValue{
    NumberValue: float64(v),
    },
    }
    case uint32:
    return &st.Value{
    Kind: &st.Value_NumberValue{
    NumberValue: float64(v),
    },
    }
    case uint64:
    return &st.Value{
    Kind: &st.Value_NumberValue{
    NumberValue: float64(v),
    },
    }
    case float32:
    return &st.Value{
    Kind: &st.Value_NumberValue{
    NumberValue: float64(v),
    },
    }
    case float64:
    return &st.Value{
    Kind: &st.Value_NumberValue{
    NumberValue: v,
    },
    }
    case string:
    return &st.Value{
    Kind: &st.Value_StringValue{
    StringValue: v,
    },
    }
    case bool:
    return &st.Value{
    Kind: &st.Value_BoolValue{
    BoolValue: v,
    },
    }
    case error:
    return &st.Value{
    Kind: &st.Value_StringValue{
    StringValue: v.Error(),
    },
    }
    case map[string]string:
    fields := make(map[string]*st.Value, len(v))
    for k, v := range v {
    fields[k] = &st.Value{
    Kind: &st.Value_StringValue{
    StringValue: v,
    },
    }
    }
    return &st.Value{
    Kind: &st.Value_StructValue{
    StructValue: &st.Struct{
    Fields: fields,
    },
    },
    }
    case map[string]interface{}:
    return &st.Value{
    Kind: &st.Value_StructValue{
    StructValue: ToStruct(v),
    },
    }
    default:
    // Fallback to reflection for other types
    value := reflect.ValueOf(v)
    switch value.Kind() {
    case reflect.Array, reflect.Slice:
    size := value.Len()
    values := make([]*st.Value, size)
    for i := 0; i < size; i++ {
    values[i] = ToValue(value.Index(i))
    }
    return &st.Value{
    Kind: &st.Value_ListValue{
    ListValue: &st.ListValue{
    Values: values,
    },
    },
    }
    case reflect.Struct:
    t := value.Type()
    size := value.NumField()
    fields := make(map[string]*st.Value, size)
    for i := 0; i < size; i++ {
    name := t.Field(i).Name
    // Better way?
    if len(name) > 0 && 'A' <= name[0] && name[0] <= 'Z' {
    fields[t.Field(i).Name] = ToValue(value.Field(i))
    }
    }
    return &st.Value{
    Kind: &st.Value_StructValue{
    StructValue: &st.Struct{
    Fields: fields,
    },
    },
    }
    case reflect.Map:
    keys := value.MapKeys()
    fields := make(map[string]*st.Value, len(keys))
    for _, k := range keys {
    if k.Kind() == reflect.String {
    fields[k.String()] = ToValue(value.MapIndex(k))
    }
    }
    return &st.Value{
    Kind: &st.Value_StructValue{
    StructValue: &st.Struct{
    Fields: fields,
    },
    },
    }
    default:
    return &st.Value{
    Kind: &st.Value_NullValue{},
    }
    }
    }
    }