1919import com .jayway .jsonpath .internal .EvaluationContext ;
2020import com .jayway .jsonpath .internal .Path ;
2121import com .jayway .jsonpath .internal .PathRef ;
22+ import com .jayway .jsonpath .spi .json .JsonProvider ;
2223import org .slf4j .Logger ;
2324import org .slf4j .LoggerFactory ;
2425
@@ -42,38 +43,93 @@ public boolean isRootPath() {
4243 }
4344
4445 @ Override
45- public EvaluationContext evaluate (Object document , Object rootDocument , Configuration configuration , boolean forUpdate ) {
46+ public EvaluationContext evaluate (Object document , Object rootDocument , Configuration configuration ,
47+ boolean forUpdate ) {
4648 if (logger .isDebugEnabled ()) {
4749 logger .debug ("Evaluating path: {}" , toString ());
4850 }
4951
50- EvaluationContextImpl ctx = new EvaluationContextImpl (this , rootDocument , configuration , forUpdate );
51- try {
52- PathRef op = ctx .forUpdate () ? PathRef .createRoot (rootDocument ) : PathRef .NO_OP ;
53-
54- if (root .isFunctionPath ()) {
55- // Remove the functionPath and evaluate the resulting path.
56- PathToken funcToken = root .chop ();
52+ EvaluationContextImpl ctx =
53+ new EvaluationContextImpl (this , rootDocument , configuration , forUpdate , rootDocument );
54+ PathRef op = ctx .forUpdate () ? PathRef .createRoot (rootDocument ) : PathRef .NO_OP ;
55+ if (root .isFunctionPath ()) {
56+ // Remove the functionPath from the path.
57+ PathToken funcToken = root .chop ();
58+ try {
59+ // Evaluate the path without the tail function.
5760 root .evaluate ("" , op , document , ctx );
5861 // Get the value of the evaluation to use as model when evaluating the function.
5962 Object arrayModel = ctx .getValue (false );
6063
61- // Evaluate the function on the model from the first evaluation.
62- RootPathToken newRoot = new RootPathToken ('x' );
63- newRoot .append (funcToken );
64- CompiledPath newCPath = new CompiledPath (newRoot , true );
65- EvaluationContextImpl newCtx = new EvaluationContextImpl (newCPath , arrayModel , configuration , false );
66- funcToken .evaluate ("" , op , arrayModel , newCtx );
67- return newCtx ;
68- } else {
64+ EvaluationContextImpl retCtx ;
65+ if (!root .isPathDefinite () && isArrayOfArrays (ctx , arrayModel )) {
66+ // Special case: non-definite paths that evaluate to an array of arrays will have the function
67+ // applied to each array. An array of the results of the function call(s) will be returned.
68+ Object array = ctx .configuration ().jsonProvider ().createArray ();
69+ for (int i = 0 ; i < ctx .configuration ().jsonProvider ().length (arrayModel ); i ++) {
70+ Object model = ctx .configuration ().jsonProvider ().getArrayIndex (arrayModel , i );
71+ EvaluationContextImpl valCtx =
72+ evaluateFunction (funcToken , model , configuration , rootDocument , op );
73+ Object val = valCtx .getValue (false );
74+ ctx .configuration ().jsonProvider ().setArrayIndex (array , i , val );
75+ }
76+
77+ retCtx = createFunctionEvaluationContext (funcToken , rootDocument , configuration , rootDocument );
78+ retCtx .addResult (root .getPathFragment (), op , array );
79+ } else {
80+ // Normal case: definite paths and non-definite paths that don't evaluate to an array of arrays
81+ // (such as those that evaluate to an array of numbers) will have the function applied to the
82+ // result of the original evaluation (which should be a 1-dimensional array). A single result
83+ // value will be returned.
84+ retCtx = evaluateFunction (funcToken , arrayModel , configuration , rootDocument , op );
85+ }
86+
87+ return retCtx ;
88+ } catch (EvaluationAbortException abort ) {
89+ } finally {
90+ // Put the functionPath back on the original path so that caching works.
91+ root .append (funcToken );
92+ }
93+ } else {
94+ try {
6995 root .evaluate ("" , op , document , ctx );
7096 return ctx ;
97+ } catch (EvaluationAbortException abort ) {
7198 }
72- } catch ( EvaluationAbortException abort ){};
99+ }
73100
74101 return ctx ;
75102 }
76103
104+ private boolean isArrayOfArrays (EvaluationContext ctx , Object model ) {
105+ // Is the model an Array containing Arrays.
106+ JsonProvider jsonProvider = ctx .configuration ().jsonProvider ();
107+ if (!jsonProvider .isArray (model )) {
108+ return false ;
109+ }
110+ if (jsonProvider .length (model ) <= 0 ) {
111+ return false ;
112+ }
113+ Object item = jsonProvider .getArrayIndex (model , 0 );
114+ return jsonProvider .isArray (item );
115+ }
116+
117+ private EvaluationContextImpl evaluateFunction (PathToken funcToken , Object model , Configuration configuration , Object rootDocument ,
118+ PathRef op ) {
119+ // Evaluate the function on the given model.
120+ EvaluationContextImpl newCtx = createFunctionEvaluationContext (funcToken , model , configuration , rootDocument );
121+ funcToken .evaluate ("" , op , model , newCtx );
122+ return newCtx ;
123+ }
124+
125+ private EvaluationContextImpl createFunctionEvaluationContext (PathToken funcToken , Object model ,
126+ Configuration configuration , Object rootDocument ) {
127+ RootPathToken newRoot = PathTokenFactory .createRootPathToken (root .getRootToken ());
128+ newRoot .append (funcToken );
129+ CompiledPath newCPath = new CompiledPath (newRoot , true );
130+ return new EvaluationContextImpl (newCPath , model , configuration , false , rootDocument );
131+ }
132+
77133 @ Override
78134 public EvaluationContext evaluate (Object document , Object rootDocument , Configuration configuration ){
79135 return evaluate (document , rootDocument , configuration , false );
0 commit comments