Files
hiscaler-gox/jsonx/parser.go
scheibling b4eb50ab55
Some checks failed
Go / build (push) Failing after 7s
Created
2025-04-08 19:16:39 +02:00

247 lines
5.6 KiB
Go

package jsonx
import (
"encoding/json"
"reflect"
"strconv"
"strings"
"git.cloudyne.io/go/hiscaler-gox/bytex"
"git.cloudyne.io/go/hiscaler-gox/stringx"
)
// Parser is a json string parse helper and not required define struct.
// You can use Find() method get the path value, and convert to string, int, int64, float32, float64, bool value.
// And you can use Exists() method check path is exists
// Usage:
// parser := jsonx.NewParser("[0,1,2]")
// parser.Find("1").Int() // Return 1, founded
// parser.Find("10", 0).Int() // Return 0 because not found, you give a default value 0
type Parser struct {
data reflect.Value
value reflect.Value
}
type ParseFinder Parser
func (pf ParseFinder) Interface() interface{} {
return pf.value.Interface()
}
func (pf ParseFinder) String() string {
switch pf.value.Kind() {
case reflect.Invalid:
return ""
default:
return stringx.String(pf.value.Interface())
}
}
func (pf ParseFinder) Float32() float32 {
switch pf.value.Kind() {
case reflect.Invalid:
return 0
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return float32(pf.value.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return float32(pf.value.Uint())
case reflect.Float32, reflect.Float64:
return float32(pf.value.Float())
case reflect.Bool:
if pf.value.Bool() {
return 1
}
return 0
case reflect.String:
d, err := strconv.ParseFloat(pf.value.String(), 32)
if err != nil {
return 0
}
return float32(d)
default:
return 0
}
}
func (pf ParseFinder) Float64() float64 {
switch pf.value.Kind() {
case reflect.Invalid:
return 0
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return float64(pf.value.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return float64(pf.value.Uint())
case reflect.Float32, reflect.Float64:
return pf.value.Float()
case reflect.Bool:
if pf.value.Bool() {
return 1
}
return 0
case reflect.String:
d, _ := strconv.ParseFloat(pf.value.String(), 64)
return d
default:
return 0
}
}
func (pf ParseFinder) Int() int {
switch pf.value.Kind() {
case reflect.Invalid:
return 0
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return int(pf.value.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return int(pf.value.Uint())
case reflect.Float32, reflect.Float64:
return int(pf.value.Float())
case reflect.Bool:
if pf.value.Bool() {
return 1
}
return 0
case reflect.String:
d, _ := strconv.Atoi(pf.value.String())
return d
default:
return 0
}
}
func (pf ParseFinder) Int64() int64 {
switch pf.value.Kind() {
case reflect.Invalid:
return 0
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return pf.value.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return int64(pf.value.Uint())
case reflect.Float32, reflect.Float64:
return int64(pf.value.Float())
case reflect.Bool:
if pf.value.Bool() {
return 1
}
return 0
case reflect.String:
d, err := strconv.ParseInt(pf.value.String(), 10, 64)
if err != nil {
return 0
}
return d
default:
return 0
}
}
func (pf ParseFinder) Bool() bool {
switch pf.value.Kind() {
case reflect.Invalid:
return false
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return pf.value.Int() > 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return pf.value.Uint() > 0
case reflect.Float32, reflect.Float64:
return pf.value.Float() > 0
case reflect.Bool:
return pf.value.Bool()
case reflect.String:
v, _ := strconv.ParseBool(pf.value.String())
return v
default:
return false
}
}
func getElement(v reflect.Value, p string) reflect.Value {
switch v.Kind() {
case reflect.Map:
vv := v.MapIndex(reflect.ValueOf(p))
if vv.Kind() == reflect.Interface {
vv = vv.Elem()
}
return vv
case reflect.Array, reflect.Slice:
if i, err := strconv.Atoi(p); err == nil {
if i >= 0 && i < v.Len() {
v = v.Index(i)
for v.Kind() == reflect.Interface {
v = v.Elem()
}
return v
}
}
}
return reflect.Value{}
}
func NewParser(s string) *Parser {
p := &Parser{}
return p.LoadString(s)
}
func (p *Parser) LoadString(s string) *Parser {
return p.LoadBytes(stringx.ToBytes(s))
}
func (p *Parser) LoadBytes(bytes []byte) *Parser {
if bytex.IsBlank(bytes) {
return p
}
var sd interface{}
if err := json.Unmarshal(bytes, &sd); err != nil {
return p
}
p.data = reflect.ValueOf(sd)
return p
}
func (p Parser) Exists(path string) bool {
if !p.data.IsValid() || path == "" {
return false
}
data := p.data
parts := strings.Split(path, ".")
n := len(parts)
for i := 0; i < n; i++ {
if data = getElement(data, parts[i]); !data.IsValid() {
return false
}
if i == n-1 {
// is last path
return true
}
}
return false
}
func (p *Parser) Find(path string, defaultValue ...interface{}) *ParseFinder {
if len(defaultValue) > 0 {
p.value = reflect.ValueOf(defaultValue[0])
}
if !p.data.IsValid() || path == "" {
return (*ParseFinder)(p)
}
data := p.data
// find the value corresponding to the path
// if any part of path cannot be located, return the default value
parts := strings.Split(path, ".")
n := len(parts)
for i := 0; i < n; i++ {
if data = getElement(data, parts[i]); !data.IsValid() {
return (*ParseFinder)(p)
}
if i == n-1 {
// is last path
p.value = data
}
}
return (*ParseFinder)(p)
}