Revision 45633

View differences:

trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.lib/org.gvsig.expressionevaluator.lib.api/src/main/java/org/gvsig/expressionevaluator/ExpressionBuilder.java
131 131

  
132 132
    public static final String FUNCTION_COLOR = "COLOR";
133 133
    
134
    public static final String FUNCTION_JSON_VALUE = "JSON_VALUE";
135
    
134 136
    public static final int PARAMETER_TYPE_CONSTANT = 0;
135 137
    public static final int PARAMETER_TYPE_VARIABLE = 1;
136 138
    
trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.lib/org.gvsig.expressionevaluator.lib.api/src/main/java/org/gvsig/expressionevaluator/spi/AbstractFunction.java
14 14
import java.util.List;
15 15
import java.util.Locale;
16 16
import java.util.Objects;
17
import java.util.logging.Level;
18
import java.util.logging.Logger;
19
import java.util.regex.Matcher;
20
import java.util.regex.Pattern;
17 21
import javax.json.JsonArray;
18 22
import javax.json.JsonObject;
19 23
import javax.json.JsonStructure;
24
import org.apache.commons.io.FilenameUtils;
20 25
import org.apache.commons.io.IOUtils;
21 26
import org.apache.commons.lang3.BooleanUtils;
22 27
import org.apache.commons.lang3.Range;
......
638 643
                return;
639 644
            }
640 645
        }
641
        InputStream is = null;
646
//        InputStream is = null;
642 647
        JSONObject json;
643 648
        try {
644
            is = url.openStream();
645
            List<String> lines = IOUtils.readLines(is);
646
            json = new JSONObject(StringUtils.join(lines,  "\n"));
649
//            is = url.openStream();
650
//            List<String> lines = IOUtils.readLines(is);
651
//            json = new JSONObject(StringUtils.join(lines,  "\n"));
652
            json = new JSONObject(IOUtils.toString(url));
647 653
        } catch (Exception ex) {
648 654
            return;
649
        } finally {
650
            IOUtils.closeQuietly(is);
655
//        } finally {
656
//            IOUtils.closeQuietly(is);
651 657
        }
652 658
        
653 659
        if( json.has("group") ) {
......
669 675
            } else {
670 676
                this.description = Objects.toString(x, null);
671 677
            }
678
            this.description = process_includes(url, this.description);
672 679
            this.description = StringUtils.replace(
673 680
                    this.description, 
674 681
                    "@@@", 
......
699 706
            }
700 707
        }
701 708
    }
709
    
710
    private String process_includes(URL pathname, String text) {
711
        Pattern p1 = Pattern.compile("[<][%]include (?<fname>[a-zA-Z0-9_$.]+)[%][>]", Pattern.DOTALL);
712
        while(true) {
713
            Matcher m = p1.matcher(text);
714
            if( m==null || !m.find()) {
715
                return text;
716
            }
717
            String path = FilenameUtils.getPathNoEndSeparator(pathname.toString());
718
            String fname = m.group("fname");
719
            URL url;
720
            String replacement;
721
            try {
722
                url = new URL(path+"/"+fname);
723
                replacement = IOUtils.toString(url);
724
//                text = m.replaceAll(replacement);
725
                StringBuilder builder = new StringBuilder(text.length()+replacement.length());
726
                builder.append(text.substring(0, m.start()));
727
                builder.append(replacement);
728
                builder.append(text.substring(m.end()));
729
                text = builder.toString();
730
            } catch (Exception ex) {
731
                return text;
732
            }
733
        }
734
    }
735
    
702 736
    protected static final int TYPE_INT =     0b00000001;
703 737
    protected static final int TYPE_LONG =    0b00000010;
704 738
    protected static final int TYPE_FLOAT =   0b00000100;
trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.lib/org.gvsig.expressionevaluator.lib.impl/src/main/resources/org/gvsig/expressionevaluator/functions/en/JSON_VALUE.json
1
{
2
    "name": "JSON_VALUE",
3
    "group": "Conversion",
4
    "template": "JSON_VALUE({{exp}}, path)",
5
    "args": [
6
        "exp - specify an expression that evaluates to a text literal. If expr is null, then the function returns null. If expr is not a text literal of well-formed JSON data using strict or lax syntax, then the function returns null by default.",
7
        "JsonPath - specify a JSON path expression. The function uses the path expression to evaluate expr and find a scalar JSON value that matches, or satisfies, the path expression. The path expression must be a text literal"
8
    ],
9
    "description": "Extracts a scalar value from a JSON string.<br>\n<%include JSON_VALUE.description.html%>",
10
    "returnType": "Object",
11
    "sqlCompatible": false
12
}
trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.lib/org.gvsig.expressionevaluator.lib.impl/src/main/resources/org/gvsig/expressionevaluator/functions/en/JSON_VALUE.description.html
1
<p>JsonPath expressions always refer to a
2
JSON structure in the same way as XPath
3
expression are used in combination
4
with an XML document. The "root member
5
object" in JsonPath is always referred to
6
as <code>$</code> regardless if it is an
7
object or array.</p>
8
<p>JsonPath expressions can use the
9
dot–notation</p>
10
<p><code>$.store.book[0].title</code></p>
11
<p>or the bracket–notation</p>
12
<p><code>$['store']['book'][0]['title']</code></p>
13
<h2>Operators</h2>
14
<table>
15
<thead>
16
<tr>
17
<th align="left">Operator</th>
18
<th align="left">Description</th>
19
</tr>
20
</thead>
21
<tbody>
22
<tr>
23
<td align="left"><code>$</code></td>
24
<td align="left">The root element to
25
query. This starts all path
26
expressions.</td>
27
</tr>
28
<tr>
29
<td align="left"><code>@</code></td>
30
<td align="left">The current node
31
being processed by a filter
32
predicate.</td>
33
</tr>
34
<tr>
35
<td align="left"><code>*</code></td>
36
<td align="left">Wildcard. Available
37
anywhere a name or numeric are
38
required.</td>
39
</tr>
40
<tr>
41
<td align="left"><code>..</code></td>
42
<td align="left">Deep scan. Available
43
anywhere a name is required.</td>
44
</tr>
45
<tr>
46
<td align="left"><code>.&lt;name&gt;</code></td>
47
<td align="left">Dot-notated child</td>
48
</tr>
49
<tr>
50
<td align="left"><code>['&lt;name&gt;'
51
(, '&lt;name&gt;')]</code></td>
52
<td align="left">Bracket-notated child
53
or children</td>
54
</tr>
55
<tr>
56
<td align="left"><code>[&lt;number&gt;
57
(, &lt;number&gt;)]</code></td>
58
<td align="left">Array index or
59
indexes</td>
60
</tr>
61
<tr>
62
<td align="left"><code>[start:end]</code></td>
63
<td align="left">Array slice operator</td>
64
</tr>
65
<tr>
66
<td align="left"><code>[?(&lt;expression&gt;)]</code></td>
67
<td align="left">Filter expression. Expression must evaluate to a boolean value.</td>
68
</tr>
69
</tbody>
70
</table>
71
<h2>Functions</h2>
72
<p>Functions can be invoked at the tail end
73
of a path - the input to a function is the
74
output of the path expression.
75
The function output is dictated by the
76
function itself.</p>
77
<table>
78
<thead>
79
<tr>
80
<th align="left">Function</th>
81
<th align="left">Description</th>
82
<th align="left">Output type</th>
83
</tr>
84
</thead>
85
<tbody>
86
<tr>
87
<td align="left">min()</td>
88
<td align="left">Provides the min value of an array of numbers</td>
89
<td align="left">Double</td>
90
</tr>
91
<tr>
92
<td align="left">max()</td>
93
<td align="left">Provides the max value of an array of numbers</td>
94
<td align="left">Double</td>
95
</tr>
96
<tr>
97
<td align="left">avg()</td>
98
<td align="left">Provides the average value of an array of numbers</td>
99
<td align="left">Double</td>
100
</tr>
101
<tr>
102
<td align="left">stddev()</td>
103
<td align="left">Provides the standard deviation value of an array of numbers</td>
104
<td align="left">Double</td>
105
</tr>
106
<tr>
107
<td align="left">length()</td>
108
<td align="left">Provides the length of an array</td>
109
<td align="left">Integer</td>
110
</tr>
111
<tr>
112
<td align="left">sum()</td>
113
<td align="left">Provides the sum value of an array of numbers</td>
114
<td align="left">Double</td>
115
</tr>
116
<tr>
117
<td align="left">keys()</td>
118
<td align="left">Provides the property keys (An alternative for terminal tilde <code>~</code>)</td>
119
<td align="left"><code>Set&lt;E&gt;</code></td>
120
</tr>
121
<tr>
122
<td align="left">concat(X)</td>
123
<td align="left">Provides a concatinated version of the path output with a new item</td>
124
<td align="left">like input</td>
125
</tr>
126
<tr>
127
<td align="left">append(X)</td>
128
<td align="left">add an item to the json path output array</td>
129
<td align="left">like input</td>
130
</tr>
131
</tbody>
132
</table>
133
<h2>Filter Operators</h2>
134
<p>Filters are logical expressions used to
135
filter arrays. A typical filter would be <code>[?(@.age&gt; 18)]</code> where <code>@</code>
136
represents the current item being
137
processed. More complex filters can be
138
created with logical operators <code>&amp;&amp;</code>
139
and <code>||</code>. String literals must
140
be enclosed by single or double quotes (<code>[?(@.color
141
== 'blue')]</code> or <code>[?(@.color
142
== "blue")]</code>).</p>
143
<table>
144
<thead>
145
<tr>
146
<th align="left">Operator</th>
147
<th align="left">Description</th>
148
</tr>
149
</thead>
150
<tbody>
151
<tr>
152
<td align="left">==</td>
153
<td align="left">left is equal to right (note that 1 is not equal to '1')</td>
154
</tr>
155
<tr>
156
<td align="left">!=</td>
157
<td align="left">left is not equal to right</td>
158
</tr>
159
<tr>
160
<td align="left">&lt;</td>
161
<td align="left">left is less than right</td>
162
</tr>
163
<tr>
164
<td align="left">&lt;=</td>
165
<td align="left">left is less or equal to right</td>
166
</tr>
167
<tr>
168
<td align="left">&gt;</td>
169
<td align="left">left is greater than right</td>
170
</tr>
171
<tr>
172
<td align="left">&gt;=</td>
173
<td align="left">left is greater than or equal to right</td>
174
</tr>
175
<tr>
176
<td align="left">=~</td>
177
<td align="left">left matches regular expression [?(@.name =~ /foo.*?/i)]</td>
178
</tr>
179
<tr>
180
<td align="left">in</td>
181
<td align="left">left exists in right [?(@.size in ['S', 'M'])]</td>
182
</tr>
183
<tr>
184
<td align="left">nin</td>
185
<td align="left">left does not exists in right</td>
186
</tr>
187
<tr>
188
<td align="left">subsetof</td>
189
<td align="left">left is a subset of right [?(@.sizes subsetof ['S', 'M', 'L'])]</td>
190
</tr>
191
<tr>
192
<td align="left">anyof</td>
193
<td align="left">left has an intersection with right [?(@.sizes anyof ['M', 'L'])]</td>
194
</tr>
195
<tr>
196
<td align="left">noneof</td>
197
<td align="left">left has no intersection with right [?(@.sizes noneof ['M', 'L'])]</td>
198
</tr>
199
<tr>
200
<td align="left">size</td>
201
<td align="left">size of left (array or string) should match right</td>
202
</tr>
203
<tr>
204
<td align="left">empty</td>
205
<td align="left">left (array or string) should be empty</td>
206
</tr>
207
</tbody>
208
</table>
209
<h2>Path Examples</h2>
210
<p>Given the json</p>
211
<pre>
212
{
213
    "store": {
214
        "book": [
215
            {
216
                "category": "reference",
217
                "author": "Nigel Rees",
218
                "title": "Sayings of the Century",
219
                "price": 8.95
220
            },
221
            {
222
                "category": "fiction",
223
                "author": "Evelyn Waugh",
224
                "title": "Sword of Honour",
225
                "price": 12.99
226
            },
227
            {
228
                "category": "fiction",
229
                "author": "Herman Melville",
230
                "title": "Moby Dick",
231
                "isbn": "0-553-21311-3",
232
                "price": 8.99
233
            },
234
            {
235
                "category": "fiction",
236
                "author": "J. R. R. Tolkien",
237
                "title": "The Lord of the Rings",
238
                "isbn": "0-395-19395-8",
239
                "price": 22.99
240
            }
241
        ],
242
        "bicycle": {
243
            "color": "red",
244
            "price": 19.95
245
        }
246
    },
247
    "expensive": 10
248
}
249
</pre>
250

  
251
<table>
252
<thead>
253
<tr>
254
<th align="left">JsonPath</th>
255
<th align="left">Result</th>
256
</tr>
257
</thead>
258
<tbody>
259
<tr>
260
<td align="left">$.store.book[*].author</td>
261
<td align="left">The authors of all books</td>
262
</tr>
263
<tr>
264
<td align="left">$..author</td>
265
<td align="left">All authors</td>
266
</tr>
267
<tr>
268
<td align="left">$.store.*</td>
269
<td align="left">All things, both books and bicycles</td>
270
</tr>
271
<tr>
272
<td align="left">$.store..price</td>
273
<td align="left">The price of everything</td>
274
</tr>
275
<tr>
276
<td align="left">$..book[2]</td>
277
<td align="left">The third book</td>
278
</tr>
279
<tr>
280
<td align="left">$..book[-2]</td>
281
<td align="left">The second to last book</td>
282
</tr>
283
<tr>
284
<td align="left">$..book[0,1]</td>
285
<td align="left">The first two books</td>
286
</tr>
287
<tr>
288
<td align="left">$..book[:2]</td>
289
<td align="left">All books from index 0 (inclusive) until index 2 (exclusive)</td>
290
</tr>
291
<tr>
292
<td align="left">$..book[1:2]</td>
293
<td align="left">All books from index 1 (inclusive) until index 2 (exclusive)</td>
294
</tr>
295
<tr>
296
<td align="left">$..book[-2:]</td>
297
<td align="left">Last two books</td>
298
</tr>
299
<tr>
300
<td align="left">$..book[2:]</td>
301
<td align="left">Book number two from tail</td>
302
</tr>
303
<tr>
304
<td align="left">$..book[?(@.isbn)]</td>
305
<td align="left">All books with an ISBN number</td>
306
</tr>
307
<tr>
308
<td align="left">$.store.book[?(@.price&lt; 10)]</td>
309
<td align="left">All books in store cheaper than 10</td>
310
</tr>
311
<tr>
312
<td align="left">$..book[?(@.price&lt;= $['expensive'])]</td>
313
<td align="left">All books in store that are not "expensive"</td>
314
</tr>
315
<tr>
316
<td align="left">$..book[?(@.author=~ /.*REES/i)]</td>
317
<td align="left">All books matching regex (ignore case)</td>
318
</tr>
319
<tr>
320
<td align="left">$..*</td>
321
<td align="left">Give me every thing</td>
322
</tr>
323
<tr>
324
<td align="left">$..book.length()</td>
325
<td align="left">The number of books</td>
326
</tr>
327
</tbody>
328
</table>
329
<br>
330
<p>You can consult the documentation of the JSON_VALUE function of Oracle and SQL Server for more information.</p>
trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.lib/org.gvsig.expressionevaluator.lib.impl/src/main/java/org/gvsig/expressionevaluator/impl/function/typeconversion/JsonValueFunction.java
22 22
 */
23 23
public class JsonValueFunction extends AbstractFunction {
24 24
    
25
    //JsonPathContextImpl implements JsonPathContext {
26
    // https://github.com/json-path/JsonPath
25
        //
26
        // function docs at:
27
        // Oracle json_value: https://docs.oracle.com/database/121/SQLRF/functions093.htm#SQLRF56668
28
        // SQLServer json_value: https://docs.microsoft.com/es-es/sql/t-sql/functions/json-value-transact-sql?view=sql-server-ver15
29
        // PostgreSQL jsonb_path_query: https://www.postgresql.org/docs/12/functions-json.html#FUNCTIONS-JSON-PROCESSING-TABLE
30
        // The implementation used is:
31
        // https://github.com/json-path/JsonPath
32
        //
33
    
27 34
    public JsonValueFunction() {
28 35
        super(GROUP_CONVERSION, FUNCTION_JSON_VALUE, Range.is(2),
29
                "Get value from a Json with a specific path.",
30
            FUNCTION_JSON_VALUE+"({{json}}, '[jsonpath]')");
31
        this.addAlias("JSON_VALUE");
36
                "finds a specified scalar JSON value in JSON data and returns it.",
37
            FUNCTION_JSON_VALUE+"({{expr}}, JsonPath)",
38
            new String[]{
39
                "expr - specify an expression that evaluates to a text literal.",
40
                "JsonPath - specify a JSON path expression."
41
            }
42
        );
32 43
    }
33 44

  
34 45
    @Override
......
38 49

  
39 50
    public static Object json_value(String json, String path) {
40 51
        JsonManager manager = ToolsUtilLocator.getJsonManager();
41
        JsonPathContext context = manager.createJSonPathContext(json);
52
        JsonPathContext context;
53
        try {
54
            context = manager.createJSonPathContext(json);
55
        } catch(Exception ex) {
56
            return null;
57
        }
42 58
        Object value = context.get(path);
43 59
        if( value instanceof List ) {
44 60
            List l = ((List)value);
......
56 72
        String path = this.getStr(args, 1);
57 73
        Object value = json_value(json, path);
58 74
        
59
        return value;   }
75
        return value;  
76
    }
60 77

  
61 78
}
trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.lib/org.gvsig.expressionevaluator.lib.impl/src/main/java/org/gvsig/expressionevaluator/impl/symboltable/UtilsSymbolTable.java
4 4
import org.gvsig.expressionevaluator.impl.function.numeric.DecrFunction;
5 5
import org.gvsig.expressionevaluator.impl.function.numeric.IncrFunction;
6 6
import org.gvsig.expressionevaluator.impl.function.programming.ColorFunction;
7
import org.gvsig.expressionevaluator.impl.function.typeconversion.JsonValueFunction;
7 8
import org.gvsig.expressionevaluator.spi.AbstractSymbolTable;
8 9

  
9 10
/**
......
20 21
        this.addFunction(new IncrFunction());
21 22
        this.addFunction(new DecrFunction());
22 23
        this.addFunction(new ColorFunction());
24
        this.addFunction(new JsonValueFunction());
23 25

  
24 26
    }
25 27

  
trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.swing/org.gvsig.expressionevaluator.swing.impl/src/main/java/org/gvsig/expressionevaluator/swing/impl/elements/DefaultFunctionElement.java
126 126
        }
127 127
        if( !StringUtils.isBlank(functionDescription) ) {
128 128
            html.append("<b>").append(i18n.getTranslation("_Description")).append("</b><br>\n");
129
            html.append(functionDescription.replace("\n", "<br>")).append("<br>\n");
129
            if( !StringUtils.containsAny(functionDescription, "<br>", "<p>", "<br/>")) {
130
                html.append(functionDescription.replace("\n", "<br>")).append("<br>\n");
131
            } else {
132
                html.append(functionDescription).append("<br>\n");
133
            }
130 134
        }            
131 135

  
132 136
        html.append("</html>\n");

Also available in: Unified diff