Skip to content

Commit

Permalink
Bracket field notation support
Browse files Browse the repository at this point in the history
  • Loading branch information
f3ath committed Jul 30, 2020
1 parent aae75ac commit 97dfab5
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 25 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
## [Unreleased]
### Added
- Bracket field notation support

## [0.0.0+dev.3] - 2020-07-28
### Added
- Partial implementation of bracket field notation
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
- [x] Basic selectors: fields, indices
- [x] Recursive descent (`$..`)
- [x] Wildcard (`$.store.*`)
- [ ] Square-bracket field notation (`['foo']`, `$['"some" \'special\' [chars]']`)
- [x] Square-bracket field notation (`['foo']`, `$['"some" \'special\' [chars]']`)
- [ ] Subscript (`books[:2]`)
- [ ] Slice (`articles[1:10:2]`)
- [ ] Union (`book[0, 1]`, `book[author, title, price]`)
Expand Down
6 changes: 4 additions & 2 deletions lib/src/quote.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/// A single-quoted string.
/// Example:
/// `hello` => `'hello'`
/// `i'm ok` => `'i\'m ok'`
/// `i'm \ok` => `'i\'m \\ok'`
class Quote {
Quote(String value) : value = "'${value.replaceAll("'", r"\'")}'";
Quote(String value)
: value =
"'" + value.replaceAll(r'\', r'\\').replaceAll("'", r"\'") + "'";

final String value;

Expand Down
43 changes: 26 additions & 17 deletions lib/src/tokenize.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,45 @@
List<String> tokenize(String expr) {
final tokens = <String>[];
var pos = 0;
var insideQuotedString = false;
while (pos < expr.length) {
var token = '';
while (pos < expr.length) {
if (_singles.contains(expr[pos])) {
final char = expr[pos];
if (char == "'") {
insideQuotedString = !insideQuotedString;
}
if (insideQuotedString) {
if (char == r'\') {
if (expr.length <= pos + 1) throw FormatException('Dangling escape');
token += expr[pos + 1];
pos += 2;
} else {
token += char;
pos += 1;
}
continue;
}
if (_singles.contains(char)) {
if (token.isNotEmpty) {
break;
}
if (expr.length > pos + 1) {
final nextTwoChars = expr[pos] + expr[pos + 1];
final nextTwoChars = char + expr[pos + 1];
if (_doubles.contains(nextTwoChars)) {
token = nextTwoChars;
pos += 2;
break;
}
}
token = expr[pos++];
token = char;
pos += 1;
break;
}
token += expr[pos++];
token += char;
pos += 1;
}
if (insideQuotedString) throw FormatException('Unmatched quote');
tokens.add(token);
}
return tokens;
Expand All @@ -31,20 +50,10 @@ const _singles = [
r'$',
'[',
']',
'(',
')',
'?',
'.',
'*',
'@',
'<',
'>',
'-',
'=',
'&',
'|',
':',
' '
];

const _doubles = ['..', '&&', '||'];
const _doubles = [
'..',
];
28 changes: 23 additions & 5 deletions test/json_path_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,29 @@ void main() {
test('Mixed brackets and fields', () {
final store = JsonPath(r"$['store'].bicycle['price']");
expect(store.toString(), r"$['store']['bicycle']['price']");
expect(store.select(json).single.value, json['store']['bicycle']['price']);
expect(
store.select(json).single.value, json['store']['bicycle']['price']);
expect(store.select(json).single.path, r"$['store']['bicycle']['price']");
});
});

group('Invalid format', () {
test('Unmatched quote', () {
expect(() => JsonPath(r"$['hello"), throwsFormatException);
expect(() => JsonPath(r"$['hello\"), throwsFormatException);
});
});

group('Uncommon brackets', () {
test('Escape single quote', () {
final j = {r"sq'sq s\s qs\'qs": 'value'};
final path = JsonPath(r"$['sq\'sq s\\s qs\\\'qs']");
final select = path.select(j);
expect(select.single.value, 'value');
expect(select.single.path, r"$['sq\'sq s\\s qs\\\'qs']");
});
});

group('Wildcards', () {
test('All in root', () {
final allInRoot = JsonPath(r'$.*');
Expand Down Expand Up @@ -87,10 +105,10 @@ void main() {
final path = JsonPath(r'$..price');
expect(path.toString(), r"$..['price']");
expect(path.select(json).length, 5);
// expect(path.select(json).first.value, json['store']);
// expect(path.select(json).first.path, r"$['store']");
// expect(path.select(json).last.value, json['store']['bicycle']['price']);
// expect(path.select(json).last.path, r"$['store']['bicycle']['price']");
expect(path.select(json).first.value, json['store']['book'][0]['price']);
expect(path.select(json).first.path, r"$['store']['book'][0]['price']");
expect(path.select(json).last.value, json['store']['bicycle']['price']);
expect(path.select(json).last.path, r"$['store']['bicycle']['price']");
});
});

Expand Down

0 comments on commit 97dfab5

Please sign in to comment.