github.com/viant/toolbox@v0.34.5/json.go (about) 1 package toolbox 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "strings" 10 ) 11 12 //IsStructuredJSON returns true if supplied represent JSON structure (map,array) 13 func IsStructuredJSON(candidate string) bool { 14 candidate = strings.Trim(candidate, "\n \t\r") 15 if candidate == "" { 16 return false 17 } 18 19 curlyStart := strings.Count(candidate, "{") 20 curlyEnd := strings.Count(candidate, "}") 21 22 squareStart := strings.Count(candidate, "[") 23 squareEnd := strings.Count(candidate, "]") 24 if !(curlyStart == curlyEnd && squareStart == squareEnd) || (curlyStart+squareStart == 0) { 25 return false 26 } 27 if !(strings.HasPrefix(candidate, "{") && strings.HasSuffix(candidate, "}") || strings.HasPrefix(candidate, "[") && strings.HasSuffix(candidate, "]")) { 28 return false 29 } 30 return json.Valid([]byte(candidate)) 31 } 32 33 //IsCompleteJSON returns true if supplied represent complete JSON 34 func IsCompleteJSON(candidate string) bool { 35 return json.Valid([]byte(candidate)) 36 } 37 38 //NewLineDelimitedJSON returns JSON for supplied multi line JSON 39 func NewLineDelimitedJSON(candidate string) ([]interface{}, error) { 40 var result = make([]interface{}, 0) 41 lines := getMultilineContent(candidate) 42 for _, line := range lines { 43 aStruct, err := JSONToInterface(line) 44 if err != nil { 45 return nil, err 46 } 47 result = append(result, aStruct) 48 } 49 return result, nil 50 } 51 52 func getMultilineContent(multiLineText string) []string { 53 multiLineText = strings.TrimSpace(multiLineText) 54 if multiLineText == "" { 55 return []string{} 56 } 57 lines := strings.Split(multiLineText, "\n") 58 var result = make([]string, 0) 59 for _, line := range lines { 60 if strings.Trim(line, " \r") == "" { 61 continue 62 } 63 result = append(result, line) 64 } 65 return result 66 } 67 68 //IsNewLineDelimitedJSON returns true if supplied content is multi line delimited JSON 69 func IsNewLineDelimitedJSON(candidate string) bool { 70 lines := getMultilineContent(candidate) 71 if len(lines) <= 1 { 72 return false 73 } 74 return IsStructuredJSON(lines[0]) && IsStructuredJSON(lines[1]) 75 } 76 77 //JSONToInterface converts JSON source to an interface (either map or slice) 78 func JSONToInterface(source interface{}) (interface{}, error) { 79 var reader io.Reader 80 switch value := source.(type) { 81 case io.Reader: 82 reader = value 83 case []byte: 84 reader = bytes.NewReader(value) 85 case string: 86 reader = strings.NewReader(value) 87 default: 88 return nil, fmt.Errorf("unsupported type: %T", source) 89 } 90 var result interface{} 91 if content, err := ioutil.ReadAll(reader); err == nil { 92 text := string(content) 93 if IsNewLineDelimitedJSON(text) { 94 return NewLineDelimitedJSON(text) 95 } 96 reader = strings.NewReader(text) 97 } 98 err := jsonDecoderFactory{}.Create(reader).Decode(&result) 99 return result, err 100 } 101 102 //JSONToMap converts JSON source into map 103 func JSONToMap(source interface{}) (map[string]interface{}, error) { 104 var reader io.Reader 105 switch value := source.(type) { 106 case io.Reader: 107 reader = value 108 case []byte: 109 reader = bytes.NewReader(value) 110 case string: 111 reader = strings.NewReader(value) 112 default: 113 return nil, fmt.Errorf("unsupported type: %T", source) 114 } 115 var result = make(map[string]interface{}) 116 err := jsonDecoderFactory{}.Create(reader).Decode(&result) 117 return result, err 118 } 119 120 //JSONToSlice converts JSON source into slice 121 func JSONToSlice(source interface{}) ([]interface{}, error) { 122 var reader io.Reader 123 switch value := source.(type) { 124 case io.Reader: 125 reader = value 126 case []byte: 127 reader = bytes.NewReader(value) 128 case string: 129 reader = strings.NewReader(value) 130 default: 131 return nil, fmt.Errorf("unsupported type: %T", source) 132 } 133 var result = make([]interface{}, 0) 134 err := jsonDecoderFactory{}.Create(reader).Decode(&result) 135 return result, err 136 } 137 138 //AsJSONText converts data structure int text JSON 139 func AsJSONText(source interface{}) (string, error) { 140 if source == nil { 141 return "", fmt.Errorf("source was nil") 142 } 143 if IsStruct(source) || IsMap(source) || IsSlice(source) { 144 buf := new(bytes.Buffer) 145 err := NewJSONEncoderFactory().Create(buf).Encode(source) 146 return buf.String(), err 147 } 148 return "", fmt.Errorf("unsupported type: %T", source) 149 } 150 151 //AsIndentJSONText converts data structure int text JSON 152 func AsIndentJSONText(source interface{}) (string, error) { 153 if IsStruct(source) || IsMap(source) || IsSlice(source) { 154 buf, err := json.MarshalIndent(source, "", "\t") 155 if err != nil { 156 return "", err 157 } 158 return string(buf), nil 159 } 160 return "", fmt.Errorf("unsupported type: %T", source) 161 } 162 163 //AnyJSONType represents any JSON type 164 type AnyJSONType string 165 166 //UnmarshalJSON implements unmarshalerinterface 167 func (s *AnyJSONType) UnmarshalJSON(b []byte) error { 168 *s = AnyJSONType(b) 169 return nil 170 } 171 172 //MarshalJSON implements marshaler interface 173 func (s *AnyJSONType) MarshalJSON() ([]byte, error) { 174 if len(*s) == 0 { 175 return []byte(`""`), nil 176 } 177 return []byte(*s), nil 178 } 179 180 //Value returns string or string slice value 181 func (s AnyJSONType) Value() (interface{}, error) { 182 var result interface{} 183 return result, json.Unmarshal([]byte(s), &result) 184 }