package cnf import ( "encoding/json" "io" "strings" ) type Json struct { data interface{} parsed bool } // Read extract a reader as its JSON representation func (d *Json) Read(_reader io.Reader) error { // 1. get json decoder decoder := json.NewDecoder(_reader) err := decoder.Decode(&d.data) if err != nil { return err } d.parsed = true return nil } // Write the JSON representation to a writer func (d *Json) Write(_writer io.Writer) error { encoder := json.NewEncoder(_writer) return encoder.Encode(&d.data) } // browse returns the target of a dot-separated path (as an interface{} chain where the last is the target if found) func (d *Json) browse(_path string) ([]interface{}, bool) { // 1. extract path path := strings.Split(_path, ".") // 2. init output chain current := d.data chain := make([]interface{}, 0, len(path)+1) chain = append(chain, current) // 3. iterate over path / nested fields for _, field := range path { fmap, ok := current.(map[string]interface{}) if !ok { // incomplete path return chain, false } child, ok := fmap[field] if !ok { // incomplete path return chain, false } current = child chain = append(chain, current) } return chain, true } // Get returns the value of a dot-separated path, and if it exists func (d *Json) Get(_path string) (string, bool) { // 1. browse path chain, found := d.browse(_path) if !found { return "", false } // 2. return if string value value, ok := chain[len(chain)-1].(string) return value, ok } // Set the value of a dot-separated path, and creates it if not found func (d *Json) Set(_path, _value string) bool { // 1. browse path + create it if does not exist path := strings.Split(_path, ".") lp := len(path) chain, found := d.browse(_path) // 2. if found -> set value if found { mapWrapper, ok := chain[len(chain)-2].(map[string]interface{}) if !ok { // impossible return false } key := path[lp-1] mapWrapper[key] = _value return true } // 3. create path until the end to set value root := make(map[string]interface{}) current := root // create children until second to last for i, l := len(chain)-1, lp-1; i < l; i++ { child := make(map[string]interface{}) current[path[i]] = child current = child } // set value current[path[lp-1]] = _value // replace whole object if empty if len(chain) < 2 { wrapper, ok := d.data.(map[string]interface{}) if !ok { // impossible return false } key := path[0] wrapper[key] = root[key] // store with key ; eitherway it will erase all brother keys return true } // update last found object to add the value wrapper, ok := chain[len(chain)-1].(map[string]interface{}) if !ok { // impossible return false } // add each subkey for subkey, subvalue := range root { wrapper[subkey] = subvalue } return true }