github.com/sacloud/iaas-api-go@v1.12.0/mapconv/map.go (about) 1 // Copyright 2022-2023 The sacloud/iaas-api-go Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package mapconv 16 17 import ( 18 "fmt" 19 "reflect" 20 "strings" 21 ) 22 23 // Map is wrapper of map[string]interface{} 24 type Map map[string]interface{} 25 26 // Map returns output map 27 func (m *Map) Map() map[string]interface{} { 28 return *m 29 } 30 31 // Set sets map value with dot-separated key 32 func (m *Map) Set(key string, value interface{}) { 33 keys := strings.Split(key, ".") 34 var dest map[string]interface{} = *m 35 for i, k := range keys { 36 last := i == len(keys)-1 37 isSlice := strings.HasPrefix(k, "[]") 38 k = strings.ReplaceAll(k, "[]", "") 39 40 var v interface{} 41 if last { 42 v = value 43 } 44 45 if !last && isSlice { 46 values := valueToSlice(value) 47 var nestMap []map[string]interface{} 48 for _, value := range values { 49 nested := Map(map[string]interface{}{}) 50 key := strings.Join(keys[i+1:], ".") 51 nested.Set(key, value) 52 nestMap = append(nestMap, nested) 53 } 54 if _, ok := dest[k]; !ok { 55 dest[k] = nestMap 56 } else { 57 existed, ok := dest[k].([]map[string]interface{}) 58 if !ok { 59 dest[k] = nestMap 60 } 61 dest[k] = append(existed, nestMap...) 62 } 63 return 64 } 65 66 setValueWithDefault(dest, k, v) 67 if !last { 68 dest = dest[k].(map[string]interface{}) 69 } 70 } 71 } 72 73 func valueToSlice(value interface{}) []interface{} { 74 v := reflect.ValueOf(value) 75 if v.Kind() == reflect.Slice { 76 ret := make([]interface{}, v.Len()) 77 for i := 0; i < v.Len(); i++ { 78 ret[i] = v.Index(i).Interface() 79 } 80 return ret 81 } 82 return []interface{}{value} 83 } 84 85 func setValueWithDefault(values map[string]interface{}, key string, value interface{}) { 86 if value == nil { 87 value = map[string]interface{}{} 88 } 89 if _, ok := values[key]; !ok { 90 values[key] = value 91 } 92 } 93 94 // Get returns map value with dot-separated key 95 func (m *Map) Get(key string) (interface{}, error) { 96 keys := strings.Split(key, ".") 97 targetMap := *m 98 for i, k := range keys { 99 last := i == len(keys)-1 100 k = strings.ReplaceAll(k, "[]", "") 101 102 value := targetMap[k] 103 if value == nil || reflect.ValueOf(value).IsZero() { 104 return nil, nil 105 } 106 if last { 107 return value, nil 108 } 109 110 switch value := value.(type) { 111 case map[string]interface{}: 112 targetMap = value 113 case []interface{}: 114 var values []interface{} 115 for _, v := range value { 116 if _, ok := v.(map[string]interface{}); !ok { 117 return nil, fmt.Errorf("elements of key %q(part of %q) are not map[string]interface{}", k, key) 118 } 119 nested := Map(v.(map[string]interface{})) 120 key := strings.Join(keys[i+1:], ".") 121 nv, err := nested.Get(key) 122 if err != nil { 123 return nil, err 124 } 125 if nv != nil { 126 nvs, ok := nv.([]interface{}) 127 if ok { 128 values = append(values, nvs...) 129 } else { 130 values = append(values, nv) 131 } 132 } 133 } 134 return values, nil 135 case []map[string]interface{}: 136 var values []interface{} 137 for _, v := range value { 138 nested := Map(v) 139 key := strings.Join(keys[i+1:], ".") 140 nv, err := nested.Get(key) 141 if err != nil { 142 return nil, err 143 } 144 if nv != nil { 145 nvs, ok := nv.([]interface{}) 146 if ok { 147 values = append(values, nvs...) 148 } else { 149 values = append(values, nv) 150 } 151 } 152 } 153 return values, nil 154 default: 155 // 対象がオブジェクト(value)、かつフィールドが全て空(nil)の場合にここに到達する 156 return nil, nil 157 } 158 } 159 160 return nil, fmt.Errorf("failed output get input map: invalid state - key:%s values:%v", key, *m) 161 }