Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a parameter skipempty to the json field type. #305

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions src/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -2325,6 +2325,85 @@ PARSER_Parse(v2IPTables)
return r;
}

/*
* Delete children of the given object if it has children and they are empty.
*
* return 0 if object is not empty
* return 1 if object is empty
* return < 0 if error
*
* Caller should do this:
* if (jsonSkipEmpty(obj) > 0) {
* json_object_put(obj);
* obj = NULL;
* }
* or otherwise not use obj if jsonSkipEmpty returns > 0.
*/
static int
jsonSkipEmpty(struct json_object *__restrict__ json)
{
int rc = 0;
struct json_object *val = NULL;

if(json == NULL) {
rc = 1;
goto finalize_it;
}

switch (json_object_get_type(json)) {
case json_type_string:
rc = json_object_get_string_len(json) == 0;
break;
case json_type_array:
{
int i;
int arrayLen = json_object_array_length(json);
for (i = 0 ; i < arrayLen ; ++i) {
val = json_object_array_get_idx(json, i);
if ((rc = jsonSkipEmpty(val)) > 0) {
/* delete the empty item and reset the index and arrayLen */
json_object_array_del_idx(json, i--);
arrayLen = json_object_array_length(json);
} else if (rc < 0) {
goto finalize_it;
}
}
rc = json_object_array_length(json) == 0;
break;
}
case json_type_object:
{
struct json_object_iterator it = json_object_iter_begin(json);
struct json_object_iterator itEnd = json_object_iter_end(json);
while (!json_object_iter_equal(&it, &itEnd)) {
val = json_object_iter_peek_value(&it);
if ((rc = jsonSkipEmpty(val)) > 0) {
json_object_object_del(json, json_object_iter_peek_name(&it));
} else if (rc < 0) {
goto finalize_it;
}
json_object_iter_next(&it);
}
rc = json_object_object_length(json) == 0;
}
case json_type_null:
case json_type_boolean:
case json_type_double:
case json_type_int:
default: break;
}
finalize_it:
return rc;
}

/*
* Parameters for field type json
* skipempty - skips empty json objects.
* - %field_name:json:skipempty%
*/
struct data_JSON {
int skipempty;
};
/**
* Parse JSON. This parser tries to find JSON data inside a message.
* If it finds valid JSON, it will extract it. Extra data after the
Expand All @@ -2340,6 +2419,7 @@ PARSER_Parse(v2IPTables)
PARSER_Parse(JSON)
const size_t i = *offs;
struct json_tokener *tokener = NULL;
struct data_JSON *const data = (struct data_JSON*) pdata;

if(npb->str[i] != '{' && npb->str[i] != ']') {
/* this can't be json, see RFC4627, Sect. 2
Expand Down Expand Up @@ -2368,6 +2448,20 @@ PARSER_Parse(JSON)
if(value == NULL) {
json_object_put(json);
} else {
if (data && data->skipempty) {
int rc = jsonSkipEmpty(json);
if (rc < 0) {
json_object_put(json);
FAIL(LN_WRONGPARSER);
} else if (rc > 0) {
/*
* json value is empty.
* E.g., {"message":""}, {"message":[]}, {"message":{}}
*/
json_object_put(json);
FAIL(0);
}
}
*value = json;
}

Expand All @@ -2376,7 +2470,40 @@ PARSER_Parse(JSON)
json_tokener_free(tokener);
return r;
}
PARSER_Construct(JSON)
{
int r = 0;
struct json_object *ed;
struct data_JSON *data = NULL;
char *flag;

if(json == NULL)
goto done;

if(json_object_object_get_ex(json, "extradata", &ed) == 0) {
/* No JSON parameter */
goto done;
}
data = (struct data_JSON*) calloc(1, sizeof(struct data_JSON));
flag = json_object_get_string(ed);
if (strcasecmp(flag, "skipempty") == 0) {
data->skipempty = 1;
} else {
ln_errprintf(ctx, 0, "invalid flag for JSON parser: %s", flag);
r = LN_BADCONFIG;
goto done;
}
*pdata = data;
done:
if(r != 0) {
free(data);
}
return r;
}
PARSER_Destruct(JSON)
{
free(pdata);
}

/* check if a char is valid inside a name of a NameValue list
* The set of valid characters may be extended if there is good
Expand Down
2 changes: 1 addition & 1 deletion src/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ PARSERDEF_NO_DATA(Time24hr);
PARSERDEF_NO_DATA(Duration);
PARSERDEF_NO_DATA(IPv4);
PARSERDEF_NO_DATA(IPv6);
PARSERDEF_NO_DATA(JSON);
PARSERDEF(JSON);
PARSERDEF_NO_DATA(CEESyslog);
PARSERDEF_NO_DATA(v2IPTables);
PARSERDEF_NO_DATA(CiscoInterfaceSpec);
Expand Down
2 changes: 1 addition & 1 deletion src/pdag.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ static struct ln_parser_info parser_lookup_table[] = {
PARSER_ENTRY_NO_DATA("duration", Duration, 16),
PARSER_ENTRY_NO_DATA("cisco-interface-spec", CiscoInterfaceSpec, 4),
PARSER_ENTRY_NO_DATA("name-value-list", NameValue, 8),
PARSER_ENTRY_NO_DATA("json", JSON, 4),
PARSER_ENTRY("json", JSON, 4),
PARSER_ENTRY_NO_DATA("cee-syslog", CEESyslog, 4),
PARSER_ENTRY_NO_DATA("mac48", MAC48, 16),
PARSER_ENTRY_NO_DATA("cef", CEF, 4),
Expand Down
1 change: 1 addition & 0 deletions tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ TESTS_SHELLSCRIPTS = \
field_rest_jsoncnf.sh \
field_json.sh \
field_json_jsoncnf.sh \
field_json_skipempty.sh \
field_cee-syslog.sh \
field_cee-syslog_jsoncnf.sh \
field_ipv6.sh \
Expand Down
30 changes: 30 additions & 0 deletions tests/field_json_skipempty.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# added 2018-08-27 by Noriko Hosoi
# This file is part of the liblognorm project, released under ASL 2.0
. $srcdir/exec.sh

test_def $0 "JSON field"
add_rule 'version=2'
add_rule 'rule=:%field:json%'

# default behaviour
execute '{"f1": "1", "f2": 2, "f3": "", "f4": {}, "f5": []}'
assert_output_json_eq '{ "field": { "f1": "1", "f2": 2 , "f3": "", "f4": {}, "f5": []} }'

# skip empty json values
reset_rules
add_rule 'version=2'
add_rule 'rule=:%field:json:skipempty%'

execute '{"f1": "1", "f2": 2, "f3": "", "f4": {}, "f5": []}'
assert_output_json_eq '{ "field": { "f1": "1", "f2": 2 } }'

# undefined parameter has to be ignored?
reset_rules
add_rule 'version=2'
add_rule 'rule=:%field:json:bogus%'

execute '{"f1": "1", "f2": 2, "f3": "", "f4": {}, "f5": []}'
assert_output_json_eq '{ "field": { "f1": "1", "f2": 2 , "f3": "", "f4": {}, "f5": []} }'

cleanup_tmp_files