// Package jsonv coerces values out of BYD's loosely typed JSON. // // The same field arrives as a JSON number in one response and a quoted string // in the next, and absent fields arrive as null rather than being omitted, so // every read goes through here rather than through a type assertion. package jsonv import ( "fmt" "math" "strconv" "strings" ) // Str renders a value as a string, mapping nil to "" rather than "". func Str(v any) string { switch t := v.(type) { case nil: return "" case string: return t case float64: // JSON has no integers, so ids and codes arrive as float64. Exponent // notation would corrupt them ("1.23456789e+09" as a user id), so // integral values are rendered as integers. if t == math.Trunc(t) && math.Abs(t) < 1e15 { return strconv.FormatInt(int64(t), 10) } return strconv.FormatFloat(t, 'g', -1, 64) case bool: return strconv.FormatBool(t) default: return fmt.Sprint(v) } } // Num coerces a value to a float. It reports false for anything that is not a // number, a bool, or a string holding a number. func Num(v any) (float64, bool) { switch t := v.(type) { case float64: return t, true case bool: if t { return 1, true } return 0, true case string: f, err := strconv.ParseFloat(strings.TrimSpace(t), 64) return f, err == nil default: return 0, false } }