├── .gitignore ├── examples ├── simple.sh ├── nested.sh └── prefix.sh ├── shon_test.go ├── README.md ├── LICENSE └── shon.go /.gitignore: -------------------------------------------------------------------------------- 1 | shon -------------------------------------------------------------------------------- /examples/simple.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 4 | shon="${dir}/../shon" 5 | payload='{"one": "1", "two": "2"}' 6 | 7 | # turn JSON payload into local variables 8 | eval $(echo $payload | $shon) 9 | 10 | # extract values 11 | echo "one -> ${one_value}" 12 | echo "two -> ${two_value}" -------------------------------------------------------------------------------- /shon_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestParseJson(t *testing.T) { 6 | b := []byte(`{"b":true,"i":1234,"st":"hi friend","map":{"hi":"ono"},"arr":[1.4,2,3,"what"]}`) 7 | r := ParseJson(b) 8 | 9 | if r.(map[string]interface{})["b"].(bool) != true { 10 | t.Errorf("I could not parse json") 11 | } 12 | } -------------------------------------------------------------------------------- /examples/nested.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 4 | shon="${dir}/../shon" 5 | payload='{"one": {"a": "ay"}, "two": {"b": "bees are so cool"}}' 6 | 7 | # turn JSON payload into local variables 8 | eval $(echo $payload | $shon) 9 | 10 | # extract values 11 | echo "one -> ${one_a_value}" 12 | echo "two -> ${two_b_value}" -------------------------------------------------------------------------------- /examples/prefix.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 4 | shon="${dir}/../shon" 5 | payload='{"one": "1", "two": "2"}' 6 | prefix="PRE_" 7 | 8 | # turn JSON payload into local variables with a prefix 9 | eval $(echo $payload | $shon | sed -e "s/^/${prefix}/") 10 | 11 | # extract values 12 | echo "one -> ${PRE_one_value}" 13 | echo "two -> ${PRE_two_value}" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![shon logo](http://nano-assets.gopagoda.io/readme-headers/shon.png)](http://nanobox.io/open-source#shon) 2 | [![Build Status](https://travis-ci.org/nanopack/shon.svg)](https://travis-ci.org/nanopack/shon) 3 | 4 | # shon 5 | 6 | Shell Object Notation 7 | 8 | A simple tool to convert json or yaml into a shell-compliant data structure. 9 | 10 | ## Status 11 | 12 | Stable/Complete 13 | 14 | ## TODO 15 | 16 | - documentation 17 | 18 | ### Contributing 19 | 20 | Contributions to the shon project are welcome and encouraged. Shon is a [Nanobox](https://nanobox.io) project and contributions should follow the [Nanobox Contribution Process & Guidelines](https://docs.nanobox.io/contributing/). 21 | 22 | ### Licence 23 | 24 | Mozilla Public License Version 2.0 25 | 26 | [![open source](http://nano-assets.gopagoda.io/open-src/nanobox-open-src.png)](http://nanobox.io/open-source) 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Nanopack 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /shon.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "reflect" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | func main() { 14 | 15 | if len(os.Args) == 2 { 16 | json := os.Args[1] 17 | body := ParseJson([]byte(json)) 18 | Output(body) 19 | } else { 20 | bytes, err := ioutil.ReadAll(os.Stdin) 21 | if err != nil { 22 | fmt.Println("error:", err) 23 | os.Exit(1) 24 | } 25 | if len(bytes) == 0 { 26 | fmt.Println("please provide json on stdin using pipes") 27 | os.Exit(1) 28 | } else { 29 | body := ParseJson(bytes) 30 | Output(body) 31 | } 32 | } 33 | 34 | } 35 | 36 | func ParseJson(b []byte) interface{} { 37 | var f interface{} 38 | err := json.Unmarshal(b, &f) 39 | if err != nil { 40 | fmt.Println("error:", err) 41 | os.Exit(1) 42 | } 43 | return f 44 | } 45 | 46 | // Consider the following JSON document: 47 | // { 48 | // "foo": "bar", 49 | // "baz": "boa" 50 | // } 51 | // An equivalent SHON structure: 52 | // nodes=foo,baz 53 | // foo_type=string 54 | // foo_value=bar 55 | // baz_type=string 56 | // baz_value=boa 57 | 58 | // ## Complex Example ## 59 | // Consider the following JSON document: 60 | // { 61 | // "foo": [ 62 | // "a", 63 | // "b", 64 | // { 65 | // "subguy": "object" 66 | // } 67 | // ], 68 | // "baz": "boa" 69 | // } 70 | // An equivalent SHON structure: 71 | // nodes=foo,baz 72 | // foo_type=array 73 | // foo_length=3 74 | // foo_0_type=string 75 | // foo_0_value=a 76 | // foo_1_type=string 77 | // foo_1_value=b 78 | // foo_2_type=node 79 | // foo_2_nodes=subguy 80 | // foo_2_subguy_type=string 81 | // foo_2_subguy_value=object 82 | // baz_type=string 83 | // baz_value=boa 84 | 85 | func Output(v interface{}) { 86 | switch v.(type) { 87 | case map[string]interface{}: 88 | OutputMap("", v) 89 | case []interface{}: 90 | OutputArray("", v) 91 | default: 92 | fmt.Println("ONOSE") 93 | os.Exit(1) 94 | } 95 | // fmt.Println(v) 96 | } 97 | 98 | func OutputMap(preface string, v interface{}) { 99 | nodes := preface + "nodes=" 100 | m := v.(map[string]interface{}) 101 | for key, value := range m { 102 | if nodes == preface+"nodes=" { 103 | nodes = nodes + key 104 | } else { 105 | nodes = nodes + "," + key 106 | } 107 | OutputSwitch(preface+key, value) 108 | } 109 | fmt.Println(nodes) 110 | 111 | } 112 | 113 | func OutputArray(preface string, v interface{}) { 114 | a := v.([]interface{}) 115 | for index, value := range a { 116 | OutputSwitch(preface+strconv.Itoa(index), value) 117 | } 118 | } 119 | 120 | func OutputSwitch(key string, v interface{}) { 121 | switch v.(type) { 122 | case map[string]interface{}: 123 | fmt.Println(key + "_type=map") 124 | OutputMap(key+"_", v) 125 | case []interface{}: 126 | fmt.Println(key + "_type=array") 127 | fmt.Println(key + "_length="+strconv.Itoa(len(v.([]interface{})))) 128 | OutputArray(key+"_", v) 129 | case string: 130 | fmt.Println(key + "_type=string") 131 | fmt.Print(key + "_value=") 132 | str := v.(string) 133 | str = strings.Replace(str, `'`, `'"'"'`, -1) 134 | fmt.Printf("'%s'\n",str) 135 | case int: 136 | fmt.Println(key + "_type=int") 137 | fmt.Println(key + "_value=" + strconv.Itoa(v.(int))) 138 | case float64: 139 | if v.(float64) == float64(int(v.(float64))) { 140 | fmt.Println(key + "_type=int") 141 | fmt.Println(key + "_value=" + strconv.Itoa(int(v.(float64)))) 142 | } else { 143 | fmt.Println(key + "_type=float") 144 | fmt.Println(key + "_value=" + strconv.FormatFloat(v.(float64), 'f', 4, 64)) 145 | } 146 | case bool: 147 | fmt.Println(key + "_type=bool") 148 | fmt.Println(key + "_value=" + strconv.FormatBool(v.(bool))) 149 | case nil: 150 | fmt.Println(key + "_type=nil") 151 | fmt.Println(key + "_value=") 152 | default: 153 | fmt.Println("I dont know how to deal with ", reflect.TypeOf(v)) 154 | os.Exit(1) 155 | } 156 | } 157 | --------------------------------------------------------------------------------