github.com/safing/portbase@v0.19.5/database/accessor/accessor-json-string.go (about)

     1  package accessor
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/tidwall/gjson"
     7  	"github.com/tidwall/sjson"
     8  )
     9  
    10  // JSONAccessor is a json string with get functions.
    11  type JSONAccessor struct {
    12  	json *string
    13  }
    14  
    15  // NewJSONAccessor adds the Accessor interface to a JSON string.
    16  func NewJSONAccessor(json *string) *JSONAccessor {
    17  	return &JSONAccessor{
    18  		json: json,
    19  	}
    20  }
    21  
    22  // Set sets the value identified by key.
    23  func (ja *JSONAccessor) Set(key string, value interface{}) error {
    24  	result := gjson.Get(*ja.json, key)
    25  	if result.Exists() {
    26  		err := checkJSONValueType(result, key, value)
    27  		if err != nil {
    28  			return err
    29  		}
    30  	}
    31  
    32  	newJSON, err := sjson.Set(*ja.json, key, value)
    33  	if err != nil {
    34  		return err
    35  	}
    36  	*ja.json = newJSON
    37  	return nil
    38  }
    39  
    40  func checkJSONValueType(jsonValue gjson.Result, key string, value interface{}) error {
    41  	switch value.(type) {
    42  	case string:
    43  		if jsonValue.Type != gjson.String {
    44  			return fmt.Errorf("tried to set field %s (%s) to a %T value", key, jsonValue.Type.String(), value)
    45  		}
    46  	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
    47  		if jsonValue.Type != gjson.Number {
    48  			return fmt.Errorf("tried to set field %s (%s) to a %T value", key, jsonValue.Type.String(), value)
    49  		}
    50  	case bool:
    51  		if jsonValue.Type != gjson.True && jsonValue.Type != gjson.False {
    52  			return fmt.Errorf("tried to set field %s (%s) to a %T value", key, jsonValue.Type.String(), value)
    53  		}
    54  	case []string:
    55  		if !jsonValue.IsArray() {
    56  			return fmt.Errorf("tried to set field %s (%s) to a %T value", key, jsonValue.Type.String(), value)
    57  		}
    58  	}
    59  	return nil
    60  }
    61  
    62  // Get returns the value found by the given json key and whether it could be successfully extracted.
    63  func (ja *JSONAccessor) Get(key string) (value interface{}, ok bool) {
    64  	result := gjson.Get(*ja.json, key)
    65  	if !result.Exists() {
    66  		return nil, false
    67  	}
    68  	return result.Value(), true
    69  }
    70  
    71  // GetString returns the string found by the given json key and whether it could be successfully extracted.
    72  func (ja *JSONAccessor) GetString(key string) (value string, ok bool) {
    73  	result := gjson.Get(*ja.json, key)
    74  	if !result.Exists() || result.Type != gjson.String {
    75  		return emptyString, false
    76  	}
    77  	return result.String(), true
    78  }
    79  
    80  // GetStringArray returns the []string found by the given json key and whether it could be successfully extracted.
    81  func (ja *JSONAccessor) GetStringArray(key string) (value []string, ok bool) {
    82  	result := gjson.Get(*ja.json, key)
    83  	if !result.Exists() && !result.IsArray() {
    84  		return nil, false
    85  	}
    86  	slice := result.Array()
    87  	sliceCopy := make([]string, len(slice))
    88  	for i, res := range slice {
    89  		if res.Type == gjson.String {
    90  			sliceCopy[i] = res.String()
    91  		} else {
    92  			return nil, false
    93  		}
    94  	}
    95  	return sliceCopy, true
    96  }
    97  
    98  // GetInt returns the int found by the given json key and whether it could be successfully extracted.
    99  func (ja *JSONAccessor) GetInt(key string) (value int64, ok bool) {
   100  	result := gjson.Get(*ja.json, key)
   101  	if !result.Exists() || result.Type != gjson.Number {
   102  		return 0, false
   103  	}
   104  	return result.Int(), true
   105  }
   106  
   107  // GetFloat returns the float found by the given json key and whether it could be successfully extracted.
   108  func (ja *JSONAccessor) GetFloat(key string) (value float64, ok bool) {
   109  	result := gjson.Get(*ja.json, key)
   110  	if !result.Exists() || result.Type != gjson.Number {
   111  		return 0, false
   112  	}
   113  	return result.Float(), true
   114  }
   115  
   116  // GetBool returns the bool found by the given json key and whether it could be successfully extracted.
   117  func (ja *JSONAccessor) GetBool(key string) (value bool, ok bool) {
   118  	result := gjson.Get(*ja.json, key)
   119  	switch {
   120  	case !result.Exists():
   121  		return false, false
   122  	case result.Type == gjson.True:
   123  		return true, true
   124  	case result.Type == gjson.False:
   125  		return false, true
   126  	default:
   127  		return false, false
   128  	}
   129  }
   130  
   131  // Exists returns the whether the given key exists.
   132  func (ja *JSONAccessor) Exists(key string) bool {
   133  	result := gjson.Get(*ja.json, key)
   134  	return result.Exists()
   135  }
   136  
   137  // Type returns the accessor type as a string.
   138  func (ja *JSONAccessor) Type() string {
   139  	return "JSONAccessor"
   140  }