...

Source file src/github.com/PaesslerAG/jsonpath/placeholder.go

Documentation: github.com/PaesslerAG/jsonpath

     1  package jsonpath
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"strconv"
     8  	"text/scanner"
     9  
    10  	"github.com/PaesslerAG/gval"
    11  )
    12  
    13  type keyValueVisitor func(key string, value interface{})
    14  
    15  type jsonObject interface {
    16  	visitElements(c context.Context, v interface{}, visit keyValueVisitor) error
    17  }
    18  
    19  type jsonObjectSlice []jsonObject
    20  
    21  type keyValuePair struct {
    22  	key   gval.Evaluable
    23  	value gval.Evaluable
    24  }
    25  
    26  type keyValueMatcher struct {
    27  	key     gval.Evaluable
    28  	matcher func(c context.Context, r interface{}, visit pathMatcher)
    29  }
    30  
    31  func parseJSONObject(ctx context.Context, p *gval.Parser) (gval.Evaluable, error) {
    32  	evals := jsonObjectSlice{}
    33  	for {
    34  		switch p.Scan() {
    35  		default:
    36  			hasWildcard := false
    37  
    38  			p.Camouflage("object", ',', '}')
    39  			key, err := p.ParseExpression(context.WithValue(ctx, hasPlaceholdersContextKey{}, &hasWildcard))
    40  			if err != nil {
    41  				return nil, err
    42  			}
    43  			if p.Scan() != ':' {
    44  				if err != nil {
    45  					return nil, p.Expected("object", ':')
    46  				}
    47  			}
    48  			e, err := parseJSONObjectElement(ctx, p, hasWildcard, key)
    49  			if err != nil {
    50  				return nil, err
    51  			}
    52  			evals.addElements(e)
    53  		case ',':
    54  		case '}':
    55  			return evals.evaluable, nil
    56  		}
    57  	}
    58  }
    59  
    60  func parseJSONObjectElement(ctx context.Context, gParser *gval.Parser, hasWildcard bool, key gval.Evaluable) (jsonObject, error) {
    61  	if hasWildcard {
    62  		p := newParser(gParser)
    63  		switch gParser.Scan() {
    64  		case '$':
    65  		case '@':
    66  			p.appendPlainSelector(currentElementSelector())
    67  		default:
    68  			return nil, p.Expected("JSONPath key and value")
    69  		}
    70  
    71  		if err := p.parsePath(ctx); err != nil {
    72  			return nil, err
    73  		}
    74  		return keyValueMatcher{key, p.path.visitMatchs}, nil
    75  	}
    76  	value, err := gParser.ParseExpression(ctx)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	return keyValuePair{key, value}, nil
    81  }
    82  
    83  func (kv keyValuePair) visitElements(c context.Context, v interface{}, visit keyValueVisitor) error {
    84  	value, err := kv.value(c, v)
    85  	if err != nil {
    86  		return err
    87  	}
    88  	key, err := kv.key.EvalString(c, v)
    89  	if err != nil {
    90  		return err
    91  	}
    92  	visit(key, value)
    93  	return nil
    94  }
    95  
    96  func (kv keyValueMatcher) visitElements(c context.Context, v interface{}, visit keyValueVisitor) (err error) {
    97  	kv.matcher(c, v, func(keys []interface{}, match interface{}) {
    98  		key, er := kv.key.EvalString(context.WithValue(c, placeholdersContextKey{}, keys), v)
    99  		if er != nil {
   100  			err = er
   101  		}
   102  		visit(key, match)
   103  	})
   104  	return
   105  }
   106  
   107  func (j *jsonObjectSlice) addElements(e jsonObject) {
   108  	*j = append(*j, e)
   109  }
   110  
   111  func (j jsonObjectSlice) evaluable(c context.Context, v interface{}) (interface{}, error) {
   112  	vs := map[string]interface{}{}
   113  
   114  	err := j.visitElements(c, v, func(key string, value interface{}) { vs[key] = value })
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  	return vs, nil
   119  }
   120  
   121  func (j jsonObjectSlice) visitElements(ctx context.Context, v interface{}, visit keyValueVisitor) (err error) {
   122  	for _, e := range j {
   123  		if err := e.visitElements(ctx, v, visit); err != nil {
   124  			return err
   125  		}
   126  	}
   127  	return nil
   128  }
   129  
   130  func parsePlaceholder(c context.Context, p *gval.Parser) (gval.Evaluable, error) {
   131  	hasWildcard := c.Value(hasPlaceholdersContextKey{})
   132  	if hasWildcard == nil {
   133  		return nil, fmt.Errorf("JSONPath placeholder must only be used in an JSON object key")
   134  	}
   135  	*(hasWildcard.(*bool)) = true
   136  	switch p.Scan() {
   137  	case scanner.Int:
   138  		id, err := strconv.Atoi(p.TokenText())
   139  		if err != nil {
   140  			return nil, err
   141  		}
   142  		return placeholder(id).evaluable, nil
   143  	default:
   144  		p.Camouflage("JSONPath placeholder")
   145  		return allPlaceholders.evaluable, nil
   146  	}
   147  }
   148  
   149  type hasPlaceholdersContextKey struct{}
   150  
   151  type placeholdersContextKey struct{}
   152  
   153  type placeholder int
   154  
   155  const allPlaceholders = placeholder(-1)
   156  
   157  func (key placeholder) evaluable(c context.Context, v interface{}) (interface{}, error) {
   158  	wildcards, ok := c.Value(placeholdersContextKey{}).([]interface{})
   159  	if !ok || len(wildcards) <= int(key) {
   160  		return nil, fmt.Errorf("JSONPath placeholder #%d is not available", key)
   161  	}
   162  	if key == allPlaceholders {
   163  		sb := bytes.Buffer{}
   164  		sb.WriteString("$")
   165  		quoteWildcardValues(&sb, wildcards)
   166  		return sb.String(), nil
   167  	}
   168  	return wildcards[int(key)], nil
   169  }
   170  
   171  func quoteWildcardValues(sb *bytes.Buffer, wildcards []interface{}) {
   172  	for _, w := range wildcards {
   173  		if wildcards, ok := w.([]interface{}); ok {
   174  			quoteWildcardValues(sb, wildcards)
   175  			continue
   176  		}
   177  		sb.WriteString(fmt.Sprintf("[%v]",
   178  			strconv.Quote(fmt.Sprint(w)),
   179  		))
   180  	}
   181  }
   182  

View as plain text