internal/pkg/file/uploader/jsdict.go (82 lines of code) (raw):

// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one // or more contributor license agreements. Licensed under the Elastic License; // you may not use this file except in compliance with the Elastic License. package uploader import ( "encoding/json" "errors" "fmt" "io" "strings" ) func ReadDict(r io.Reader) (JSDict, error) { var dict JSDict decoder := json.NewDecoder(r) decoder.UseNumber() // can directly parse numbers from JSON -> int64 instead of float64 in-between return dict, decoder.Decode(&dict) } // helper for accessing nested properties without panics // it allows for a safe way to do things like // js["foo"].(map[string]interface{})["bar"].(map[string]interface{})["baz"].(string) type JSDict map[string]interface{} // for a given path, retrieves the raw value in the json structure // safely, such that if any key is missing, or if any non-leaf key // is not an object, returns false instead of panicking func (j JSDict) Val(keys ...string) (interface{}, bool) { if len(keys) == 0 { return nil, false } var m map[string]interface{} = j for i, k := range keys { value, ok := m[k] if !ok { return nil, false } if i == len(keys)-1 { return value, true } m, ok = value.(map[string]interface{}) if !ok { return nil, false } } return nil, false } // convenience for safely requesting a nested string value func (j JSDict) Str(keys ...string) (string, bool) { if val, ok := j.Val(keys...); ok { s, ok := val.(string) return s, ok } return "", false } // convenience for safely requesting a nested int64 value func (j JSDict) Int64(keys ...string) (int64, bool) { if val, ok := j.Val(keys...); ok { switch v := val.(type) { case float64: // standard json decode/unmarshal return int64(v), true case json.Number: // json UseNumber() to get int64 directly n, err := v.Int64() return n, err == nil case int: return int64(v), true case int64: return v, true default: return 0, false } } return 0, false } // write values to possibly nested locations func (j JSDict) Put(value interface{}, keys ...string) error { if len(keys) == 0 { return errors.New("path not provided") } // simple case if len(keys) == 1 { j[keys[0]] = value return nil } var m map[string]interface{} = j for i, k := range keys { if i == len(keys)-1 { m[k] = value return nil } // otherwise, we have more to nest. Make sure this level is an object x, ok := m[k].(map[string]interface{}) if !ok { return fmt.Errorf("unable to write to %s, missing property at %s", strings.Join(keys, "."), k) } m = x } return nil }