github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/utils/json/unmarshal.go (about) 1 package json 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "strconv" 8 "strings" 9 10 "github.com/lmorg/murex/utils/mxjson" 11 ) 12 13 // Unmarshal is a wrapper around the standard json.Unmarshal function. This is 14 // done this way so that murex can swap out the JSON unmarshaller from the 15 // standard libraries with a 3rd party decoder that might run more efficiently. 16 func Unmarshal(data []byte, v interface{}) (err error) { 17 //err = gojay.Unmarshal(data, v) 18 //if err == nil { 19 // return 20 //} 21 22 return json.Unmarshal(data, v) 23 } 24 25 // UnmarshalMurex is a wrapper around Go's JSON unmarshaller to support nested 26 // brace quotes (which allows for a cleaner syntax when embedding Murex code as 27 // JSON strings) and line comments via the hash, `#`, prefix. 28 func UnmarshalMurex(data []byte, v interface{}) error { 29 err := unmarshalMurex(data, v) 30 if err == nil { 31 return nil 32 } 33 34 _, mxerr := mxjson.Parse(data) 35 return fmt.Errorf("mxjson parse error: %s\n%s", err, mxerr) 36 } 37 38 func unmarshalMurex(data []byte, v interface{}) error { 39 var ( 40 escape bool 41 comment bool 42 comments = make([]string, 1) 43 single bool 44 double bool 45 brace int 46 replace []string 47 pop []byte 48 ) 49 50 for i := range data { 51 if brace > 0 { 52 pop = append(pop, data[i]) 53 } 54 55 switch data[i] { 56 case '\\': 57 switch { 58 case comment: 59 comments[len(comments)-1] += string(data[i]) 60 default: 61 escape = !escape 62 } 63 64 case '#': 65 switch { 66 case escape: 67 escape = false 68 case single, double, brace > 0: 69 // do nothing 70 default: 71 comment = true 72 comments[len(comments)-1] += string(data[i]) 73 } 74 75 case '\'': 76 switch { 77 case comment: 78 comments[len(comments)-1] += string(data[i]) 79 case escape: 80 escape = false 81 case double, brace > 0: 82 // do nothing 83 case single: 84 single = false 85 default: 86 single = true 87 } 88 89 case '"': 90 switch { 91 case comment: 92 comments[len(comments)-1] += string(data[i]) 93 case escape: 94 escape = false 95 case single, brace > 0: 96 // do nothing 97 case double: 98 double = false 99 default: 100 double = true 101 } 102 103 case '(': 104 switch { 105 case comment: 106 comments[len(comments)-1] += string(data[i]) 107 case escape: 108 escape = false 109 case single, double: 110 // do nothing 111 case brace == 0: 112 //pop = append(pop, data[i]) 113 brace++ 114 default: 115 brace++ 116 } 117 118 case ')': 119 switch { 120 case comment: 121 comments[len(comments)-1] += string(data[i]) 122 case escape: 123 escape = false 124 case single, double: 125 // do nothing 126 case brace == 1: 127 replace = append(replace, string(pop[:len(pop)-1])) 128 pop = []byte{} 129 brace-- 130 default: 131 brace-- 132 } 133 134 case '\n': 135 switch { 136 case comment: 137 comment = false 138 comments = append(comments, "") 139 case escape: 140 escape = false 141 } 142 143 default: 144 switch { 145 case comment: 146 comments[len(comments)-1] += string(data[i]) 147 default: 148 escape = false 149 } 150 } 151 152 } 153 154 switch { 155 case comment: 156 comments = append(comments, "") 157 case single: 158 return errors.New("unterminated single quotes") 159 case double: 160 return errors.New("unterminated double quotes") 161 case brace > 0: 162 return fmt.Errorf("%d more open brace(s) than closed", brace) 163 case brace < 0: 164 return fmt.Errorf("%d more closed brace(s) than opened", brace) 165 } 166 167 // nothing to do so might as well just forward the params on without editing 168 if len(replace) == 0 && len(comments) == 1 { 169 return json.Unmarshal(data, v) 170 } 171 172 s := string(data) 173 174 for i := range comments { 175 s = strings.Replace(s, comments[i], "", 1) 176 } 177 178 for i := range replace { 179 if len(replace[i]) > 0 { 180 s = strings.Replace(s, "("+replace[i]+")", strconv.Quote(replace[i]), 1) 181 } 182 } 183 184 return Unmarshal([]byte(s), v) 185 }