2 * Copyright (C) 2013-2014 Jo-Philipp Wich <jo@mein.io>
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26 #include <json-c/json.h>
29 #include <libubox/list.h>
37 struct json_object
*jsobj
;
38 struct list_head list
;
42 print_usage(char *app
)
46 " # %s [-a] [-i <file> | -s \"json...\"] {-t <pattern> | -e <pattern>}\n"
47 " -q Quiet, no errors are printed\n"
48 " -h, --help Print this help\n"
49 " -a Implicitely treat input as array, useful for JSON logs\n"
50 " -i path Specify a JSON file to parse\n"
51 " -s \"json\" Specify a JSON string to parse\n"
52 " -l limit Specify max number of results to show\n"
53 " -F separator Specify a field separator when using export\n"
54 " -t <pattern> Print the type of values matched by pattern\n"
55 " -e <pattern> Print the values matched by pattern\n"
56 " -e VAR=<pat> Serialize matched value for shell \"eval\"\n\n"
58 " Patterns are JsonPath: http://goessner.net/articles/JsonPath/\n"
59 " This tool implements $, @, [], * and the union operator ','\n"
60 " plus the usual expressions and literals.\n"
61 " It does not support the recursive child search operator '..' or\n"
62 " the '?()' and '()' filter expressions as those would require a\n"
63 " complete JavaScript engine to support them.\n\n"
65 " Display the first IPv4 address on lan:\n"
66 " # ifstatus lan | %s -e '@[\"ipv4-address\"][0].address'\n\n"
67 " Extract the release string from the board information:\n"
68 " # ubus call system board | %s -e '@.release.description'\n\n"
69 " Find all interfaces which are up:\n"
70 " # ubus call network.interface dump | \\\n"
71 " %s -e '@.interface[@.up=true].interface'\n\n"
72 " Export br-lan traffic counters for shell eval:\n"
73 " # devstatus br-lan | %s -e 'RX=@.statistics.rx_bytes' \\\n"
74 " -e 'TX=@.statistics.tx_bytes'\n",
75 app
, app
, app
, app
, app
);
78 static struct json_object
*
79 parse_json_chunk(struct json_tokener
*tok
, struct json_object
*array
,
80 const char *buf
, size_t len
, enum json_tokener_error
*err
)
82 struct json_object
*obj
= NULL
;
86 obj
= json_tokener_parse_ex(tok
, buf
, len
);
87 *err
= json_tokener_get_error(tok
);
89 if (*err
== json_tokener_success
)
93 json_object_array_add(array
, obj
);
100 else if (*err
!= json_tokener_continue
)
105 buf
+= tok
->char_offset
;
106 len
-= tok
->char_offset
;
112 static struct json_object
*
113 parse_json(FILE *fd
, const char *source
, const char **error
, bool array_mode
)
117 struct json_object
*obj
= NULL
, *array
= NULL
;
118 struct json_tokener
*tok
= json_tokener_new();
119 enum json_tokener_error err
= json_tokener_continue
;
123 *error
= "Out of memory";
129 array
= json_object_new_array();
133 json_tokener_free(tok
);
134 *error
= "Out of memory";
141 obj
= parse_json_chunk(tok
, array
, source
, strlen(source
), &err
);
145 while ((len
= fread(buf
, 1, sizeof(buf
), fd
)) > 0)
147 obj
= parse_json_chunk(tok
, array
, buf
, len
, &err
);
149 if (err
== json_tokener_success
&& !array
)
152 if (err
!= json_tokener_continue
)
157 json_tokener_free(tok
);
161 if (err
== json_tokener_continue
)
162 err
= json_tokener_error_parse_eof
;
164 *error
= json_tokener_error_desc(err
);
168 return array
? array
: obj
;
172 print_string(const char *s
)
190 print_separator(const char *sep
, int *sc
, int sl
)
194 switch (sep
[(*sc
- 1) % sl
])
209 printf("%c", sep
[(*sc
- 1) % sl
]);
217 export_value(struct list_head
*matches
, const char *prefix
, const char *sep
,
221 int sc
= 0, sl
= strlen(sep
);
222 struct match_item
*item
;
224 if (list_empty(matches
))
229 printf("export %s=", prefix
);
231 list_for_each_entry(item
, matches
, list
)
236 switch (json_object_get_type(item
->jsobj
))
238 case json_type_object
:
239 ; /* a label can only be part of a statement */
240 json_object_object_foreach(item
->jsobj
, key
, val
)
245 print_separator(sep
, &sc
, sl
);
250 case json_type_array
:
251 for (n
= 0, len
= json_object_array_length(item
->jsobj
);
254 print_separator(sep
, &sc
, sl
);
259 case json_type_boolean
:
260 print_separator(sep
, &sc
, sl
);
261 printf("%d", json_object_get_boolean(item
->jsobj
));
265 print_separator(sep
, &sc
, sl
);
266 printf("%" PRId64
, json_object_get_int64(item
->jsobj
));
269 case json_type_double
:
270 print_separator(sep
, &sc
, sl
);
271 printf("%f", json_object_get_double(item
->jsobj
));
274 case json_type_string
:
275 print_separator(sep
, &sc
, sl
);
276 print_string(json_object_get_string(item
->jsobj
));
288 list_for_each_entry(item
, matches
, list
)
293 switch (json_object_get_type(item
->jsobj
))
295 case json_type_object
:
296 case json_type_array
:
297 case json_type_boolean
:
299 case json_type_double
:
300 printf("%s\n", json_object_to_json_string(item
->jsobj
));
303 case json_type_string
:
304 printf("%s\n", json_object_get_string(item
->jsobj
));
315 export_type(struct list_head
*matches
, const char *prefix
, int limit
)
318 struct match_item
*item
;
319 const char *types
[] = {
329 if (list_empty(matches
))
333 printf("export %s=", prefix
);
335 list_for_each_entry(item
, matches
, list
)
343 printf("%s", types
[json_object_get_type(item
->jsobj
)]);
354 match_cb(struct json_object
*res
, void *priv
)
356 struct list_head
*h
= priv
;
357 struct match_item
*i
= calloc(1, sizeof(*i
));
362 list_add_tail(&i
->list
, h
);
367 print_error(struct jp_state
*state
, char *expr
)
372 fprintf(stderr
, "Syntax error: ");
374 switch (state
->error_code
)
377 fprintf(stderr
, "Unexpected character\n");
381 fprintf(stderr
, "String or label literal too long\n");
385 fprintf(stderr
, "Invalid escape sequence\n");
389 fprintf(stderr
, "Unterminated string\n");
393 for (i
= 0; i
< sizeof(state
->error_code
) * 8; i
++)
395 if (state
->error_code
& (1 << i
))
398 first
? "Expecting %s" : " or %s", tokennames
[i
]);
404 fprintf(stderr
, "\n");
408 fprintf(stderr
, "In expression %s\n", expr
);
409 fprintf(stderr
, "Near here ----");
411 for (i
= 0; i
< state
->error_pos
; i
++)
412 fprintf(stderr
, "-");
414 fprintf(stderr
, "^\n");
418 filter_json(int opt
, struct json_object
*jsobj
, char *expr
, const char *sep
,
421 struct jp_state
*state
;
422 const char *prefix
= NULL
;
423 struct list_head matches
;
424 struct match_item
*item
, *tmp
;
425 struct json_object
*res
= NULL
;
427 state
= jp_parse(expr
);
431 fprintf(stderr
, "Out of memory\n");
434 else if (state
->error_code
)
436 print_error(state
, expr
);
440 INIT_LIST_HEAD(&matches
);
442 res
= jp_match(state
->path
, jsobj
, match_cb
, &matches
);
443 prefix
= (state
->path
->type
== T_LABEL
) ? state
->path
->str
: NULL
;
448 export_type(&matches
, prefix
, limit
);
452 export_value(&matches
, prefix
, sep
, limit
);
456 list_for_each_entry_safe(item
, tmp
, &matches
, list
)
466 int main(int argc
, char **argv
)
468 bool array_mode
= false;
469 int opt
, rv
= 0, limit
= 0x7FFFFFFF;
471 struct json_object
*jsobj
= NULL
;
472 const char *jserr
= NULL
, *source
= NULL
, *separator
= " ";
476 print_usage(argv
[0]);
480 while ((opt
= getopt(argc
, argv
, "ahi:s:e:t:F:l:q")) != -1)
489 print_usage(argv
[0]);
493 input
= fopen(optarg
, "r");
497 fprintf(stderr
, "Failed to open %s: %s\n",
498 optarg
, strerror(errno
));
511 if (optarg
&& *optarg
)
516 limit
= atoi(optarg
);
523 jsobj
= parse_json(input
, source
, &jserr
, array_mode
);
527 fprintf(stderr
, "Failed to parse json data: %s\n",
535 if (!filter_json(opt
, jsobj
, optarg
, separator
, limit
))
548 json_object_put(jsobj
);
550 if (input
&& input
!= stdin
)