package xvdoc import ( "encoding/json" "fmt" "reflect" "strconv" "github.com/xuri/excelize/v2" ) type JSONMap map[string]interface{} func (s JSONMap) Bool(key string, defaultVal bool) bool { if res, check := s[key].(bool); check { return res } return defaultVal } func (s JSONMap) Int(key string, defaultVal int64) int64 { if iface, check := s[key]; check { rVal := reflect.ValueOf(iface) switch rVal.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return rVal.Int() case reflect.Float32, reflect.Float64: return int64(rVal.Float()) } } return defaultVal } func (s JSONMap) Value(key string, defaultVal interface{}) interface{} { if iface, check := s[key]; check { dVal := reflect.ValueOf(defaultVal) if !dVal.IsValid() { return iface } else { lVal := reflect.ValueOf(iface) if !lVal.IsValid() { return defaultVal } else if lVal.Kind() == dVal.Kind() { return iface } else if lVal.Type().ConvertibleTo(dVal.Type()) { return lVal.Convert(dVal.Type()).Interface() } else { if lVal.Kind() == reflect.String { switch dVal.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if val, err := strconv.Atoi(lVal.String()); err != nil { return defaultVal } else { lVal = reflect.ValueOf(val) return lVal.Convert(dVal.Type()) } case reflect.Float32, reflect.Float64: if val, err := strconv.ParseFloat(lVal.String(), 64); err != nil { return defaultVal } else { lVal = reflect.ValueOf(val) return lVal.Convert(dVal.Type()) } } } } } } return defaultVal } func (s JSONMap) StringVal(key, defaultVal string) string { if iface, check := s[key]; check { return fmt.Sprint(iface) } return defaultVal } func (s JSONMap) Interface(key string) interface{} { return s[key] } func (s JSONMap) StringArray(key string) (res []string) { if arr, check := s[key].([]interface{}); check { res = make([]string, len(arr)) for i, v := range arr { res[i] = fmt.Sprint(v) } } return } func (s JSONMap) Array(key string) (res []interface{}) { if arr, check := s[key].([]interface{}); check { res = arr } return } func (s JSONMap) JSONMap(key string) (res JSONMap) { if m, check := s[key].(map[string]interface{}); check { res = JSONMap(m) } return } func (s JSONMap) KeyExists(key string) (check bool) { _, check = s[key] return check } func (s JSONMap) Copy() JSONMap { res := make(JSONMap) for key, val := range s { res[key] = val } return res } func (s JSONMap) JSON() (res []byte) { res, _ = json.Marshal(&s) return } func (s JSONMap) Map() map[string]interface{} { return map[string]interface{}(s) } func copyMap(m map[string]interface{}) (res map[string]interface{}) { res = make(map[string]interface{}) for key, val := range m { switch val.(type) { case []interface{}: res[key] = copySlice(val.([]interface{})) case map[string]interface{}: res[key] = copyMap(val.(map[string]interface{})) default: res[key] = val } } return } func copySlice(arr []interface{}) (res []interface{}) { res = make([]interface{}, len(arr)) for i, v := range arr { switch v.(type) { case []interface{}: res[i] = copySlice(v.([]interface{})) case map[string]interface{}: res[i] = copyMap(v.(map[string]interface{})) default: res[i] = v } } return } func mergeSlices(aSource, aMerge []interface{}) []interface{} { return append(aSource, aMerge...) } func mergeMaps(mSource, mMerge map[string]interface{}) (res map[string]interface{}) { res = copyMap(mSource) // merge result with result map for key, val := range mMerge { if sVal, check := res[key]; check { switch val.(type) { case map[string]interface{}: { if m, check := sVal.(map[string]interface{}); check { res[key] = mergeMaps(m, val.(map[string]interface{})) } else if val == nil { res[key] = val } } case []interface{}: { if arr, check := sVal.([]interface{}); check { res[key] = mergeSlices(arr, val.([]interface{})) } else if val == nil { res[key] = val } } default: res[key] = val } } else { switch val.(type) { case map[string]interface{}: res[key] = copyMap(val.(map[string]interface{})) case []interface{}: res[key] = copySlice(val.([]interface{})) default: res[key] = val } } } return } func (s JSONMap) ToGraphicOptions() (res *excelize.GraphicOptions) { res = &excelize.GraphicOptions{ LockAspectRatio: s.Bool("lock_aspect_ratio", false), AutoFit: s.Bool("autofit", false), OffsetX: int(s.Int("offset_x", 0)), OffsetY: int(s.Int("offset_y", 0)), Hyperlink: s.StringVal("link", ""), HyperlinkType: s.StringVal("link_type", ""), Positioning: s.StringVal("positioning", ""), } return } func (s JSONMap) ToStyle() (res *excelize.Style) { res = &excelize.Style{} // borders if arr := s.Array("border"); len(arr) > 0 { for _, val := range arr { if m, check := val.(map[string]any); check { jm := JSONMap(m) res.Border = append(res.Border, excelize.Border{ Type: jm.StringVal("type", "top"), Color: jm.StringVal("color", "#000000"), Style: int(jm.Int("style", 0)), }) } } } if s.KeyExists("fill") { mf := s.JSONMap("fill") res.Fill = excelize.Fill{ Type: mf.StringVal("type", ""), Pattern: int(mf.Int("pattern", 0)), } for _, color := range mf.Array("color") { res.Fill.Color = append(res.Fill.Color, fmt.Sprint(color)) } // Shading ??? } if s.KeyExists("font") { mf := s.JSONMap("font") res.Font = &excelize.Font{ Bold: mf.Bool("bold", false), Italic: mf.Bool("italic", false), Underline: mf.StringVal("underline", ""), Family: mf.StringVal("family", ""), Size: float64(mf.Int("size", 0)), Strike: mf.Bool("strike", false), Color: mf.StringVal("color", "#000000"), //VertAlign: ???, } } if s.KeyExists("alignment") { ma := s.JSONMap("alignment") res.Alignment = &excelize.Alignment{ Horizontal: ma.StringVal("horizontal", ""), Vertical: ma.StringVal("vertical", ""), WrapText: ma.Bool("wrap", false), } } return } func mapJSON(m map[string]interface{}) (res []byte) { res, _ = json.Marshal(&m) return }