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  }