diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c69295..d2641d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,11 @@ ## [Unreleased] +## [0.1.2] - 2020-09-06 +### Changed +- When JsonPath.set() is called on a path with non-existing property, the property will be created. +Previously, no modification would be made and no errors/exceptions thrown. +- When JsonPath.set() is called on a path with non-existing index, a `RangeError` will be thrown. +Previously, no modification would be made and no errors/exceptions thrown. + ## [0.1.1] - 2020-09-05 ### Fixed - Fixed example code in the readme @@ -13,7 +20,7 @@ ## [0.0.2] - 2020-09-01 ### Fixed -- Last element of array was not selected (regression #1) +- Last element of array would not get selected (regression #1) ## [0.0.1] - 2020-08-03 ### Added @@ -53,7 +60,8 @@ ### Added - Basic design draft -[Unreleased]: https://github.com/f3ath/jessie/compare/0.1.1...HEAD +[Unreleased]: https://github.com/f3ath/jessie/compare/0.1.2...HEAD +[0.1.2]: https://github.com/f3ath/jessie/compare/0.1.1...0.1.2 [0.1.1]: https://github.com/f3ath/jessie/compare/0.1.0...0.1.1 [0.1.0]: https://github.com/f3ath/jessie/compare/0.0.2...0.1.0 [0.0.2]: https://github.com/f3ath/jessie/compare/0.0.1...0.0.2 diff --git a/lib/src/json_path.dart b/lib/src/json_path.dart index da2bc26..03bbbd6 100644 --- a/lib/src/json_path.dart +++ b/lib/src/json_path.dart @@ -33,8 +33,7 @@ class JsonPath { _selector.read([JsonPathMatch(json, '')]); /// Returns a copy of [json] with all matching values replaced with [value]. - dynamic set(dynamic json, dynamic value) => - _selector.replace(json, (_) => value); + dynamic set(dynamic json, dynamic value) => _selector.set(json, (_) => value); @override String toString() => _selector.expression(); diff --git a/lib/src/selector/filter.dart b/lib/src/selector/filter.dart index ec89f30..238371a 100644 --- a/lib/src/selector/filter.dart +++ b/lib/src/selector/filter.dart @@ -19,6 +19,6 @@ class Filter with SelectorMixin implements Selector { String expression() => '[?$name]'; @override - dynamic replace(dynamic json, Replacement replacement) => + dynamic set(dynamic json, Replacement replacement) => isApplicable(json) ? replacement(json) : json; } diff --git a/lib/src/selector/joint.dart b/lib/src/selector/joint.dart index 6771e98..9102b63 100644 --- a/lib/src/selector/joint.dart +++ b/lib/src/selector/joint.dart @@ -34,6 +34,6 @@ class Joint with SelectorMixin implements Selector { right.expression(); @override - dynamic replace(dynamic json, Replacement replacement) => - left.replace(json, (_) => right.replace(_, replacement)); + dynamic set(dynamic json, Replacement replacement) => + left.set(json, (_) => right.set(_, replacement)); } diff --git a/lib/src/selector/list_union.dart b/lib/src/selector/list_union.dart index feb9c5f..360a322 100644 --- a/lib/src/selector/list_union.dart +++ b/lib/src/selector/list_union.dart @@ -20,13 +20,9 @@ class ListUnion with SelectorMixin implements Selector { .map((key) => JsonPathMatch(list[key], path + '[$key]')); @override - dynamic replace(dynamic json, Function(dynamic _) replacement) { + dynamic set(dynamic json, Function(dynamic _) replacement) { if (json is List) { - final applicableKeys = keys.where((key) => json.length > key); - if (applicableKeys.isEmpty) { - return json; - } - return _replaceInList(json, applicableKeys, replacement); + return _replaceInList(json, keys, replacement); } return json; } diff --git a/lib/src/selector/list_wildcard.dart b/lib/src/selector/list_wildcard.dart index fa39f0d..4d72282 100644 --- a/lib/src/selector/list_wildcard.dart +++ b/lib/src/selector/list_wildcard.dart @@ -13,7 +13,7 @@ class ListWildcard with SelectorMixin implements Selector { String expression() => '[*]'; @override - dynamic replace(dynamic json, Replacement replacement) => + dynamic set(dynamic json, Replacement replacement) => (json is List) ? json.map(replacement).toList() : json; Iterable _wrap(List val, String path) sync* { diff --git a/lib/src/selector/object_union.dart b/lib/src/selector/object_union.dart index 93ad390..3e6af42 100644 --- a/lib/src/selector/object_union.dart +++ b/lib/src/selector/object_union.dart @@ -18,14 +18,9 @@ class ObjectUnion with SelectorMixin implements Selector { String expression() => '[${keys.map((k) => Quote(k)).join(',')}]'; @override - dynamic replace(dynamic json, Replacement replacement) { - if (json is Map) { - final patch = _makePatch(json, replacement); - if (patch.isEmpty) { - return json; - } - return {...json, ...patch}; - } + dynamic set(dynamic json, Replacement replacement) { + if (json == null) return _patch({}, replacement); + if (json is Map) return {...json, ..._patch(json, replacement)}; return json; } @@ -33,8 +28,6 @@ class ObjectUnion with SelectorMixin implements Selector { .where(map.containsKey) .map((key) => JsonPathMatch(map[key], path + '[${Quote(key)}]')); - Map _makePatch(Map map, Replacement replacement) => - Map.fromEntries(keys - .where(map.containsKey) - .map((key) => MapEntry(key, replacement(map[key])))); + Map _patch(Map map, Replacement replacement) => + Map.fromEntries(keys.map((key) => MapEntry(key, replacement(map[key])))); } diff --git a/lib/src/selector/object_wildcard.dart b/lib/src/selector/object_wildcard.dart index dd77769..f665cee 100644 --- a/lib/src/selector/object_wildcard.dart +++ b/lib/src/selector/object_wildcard.dart @@ -24,7 +24,7 @@ class ObjectWildcard with SelectorMixin implements Selector { .map((e) => JsonPathMatch(e.value, path + '[${e.key}]')); @override - dynamic replace(dynamic json, Replacement replacement) => (json is Map) + dynamic set(dynamic json, Replacement replacement) => (json is Map) ? json.map((key, value) => MapEntry(key, replacement(value))) : json; } diff --git a/lib/src/selector/recursive.dart b/lib/src/selector/recursive.dart index 0154a8e..029a9e2 100644 --- a/lib/src/selector/recursive.dart +++ b/lib/src/selector/recursive.dart @@ -12,7 +12,7 @@ class Recursive with SelectorMixin implements Selector { String expression() => '..'; @override - dynamic replace(dynamic json, Replacement replacement) { + dynamic set(dynamic json, Replacement replacement) { if (json is Map) { return _replaceInMap(json, replacement); } @@ -33,10 +33,10 @@ class Recursive with SelectorMixin implements Selector { } dynamic _replaceInMap(Map map, Replacement replacement) => replacement( - map.map((key, value) => MapEntry(key, replace(value, replacement)))); + map.map((key, value) => MapEntry(key, set(value, replacement)))); dynamic _replaceInList(List list, Replacement replacement) => - replacement(list.map((value) => replace(value, replacement)).toList()); + replacement(list.map((value) => set(value, replacement)).toList()); Iterable _values(List val, String path) => val .asMap() diff --git a/lib/src/selector/root.dart b/lib/src/selector/root.dart index a4914a2..0e9f32a 100644 --- a/lib/src/selector/root.dart +++ b/lib/src/selector/root.dart @@ -13,5 +13,5 @@ class RootSelector with SelectorMixin implements Selector { String expression() => r'$'; @override - dynamic replace(dynamic json, Replacement replacement) => replacement(json); + dynamic set(dynamic json, Replacement replacement) => replacement(json); } diff --git a/lib/src/selector/selector.dart b/lib/src/selector/selector.dart index cbbedfd..30fa731 100644 --- a/lib/src/selector/selector.dart +++ b/lib/src/selector/selector.dart @@ -12,7 +12,7 @@ abstract class Selector { Selector then(Selector other); /// Returns a copy of [json] with all selected values modified using [replacement] function. - dynamic replace(dynamic json, Replacement replacement); + dynamic set(dynamic json, Replacement replacement); } typedef Replacement = dynamic Function(dynamic value); diff --git a/lib/src/selector/slice.dart b/lib/src/selector/slice.dart index d0aff81..984da9a 100644 --- a/lib/src/selector/slice.dart +++ b/lib/src/selector/slice.dart @@ -33,7 +33,7 @@ class Slice with SelectorMixin implements Selector { '[${first == 0 ? '' : first}:${last ?? ''}${step != 1 ? ':$step' : ''}]'; @override - dynamic replace(dynamic json, Replacement replacement) { + dynamic set(dynamic json, Replacement replacement) { if (json is List) { final indices = _indices(json); if (indices.isNotEmpty) { diff --git a/pubspec.yaml b/pubspec.yaml index 90d7084..9063d27 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: json_path -version: 0.1.1+1 +version: 0.1.2 description: Implementation of JSONPath expressions like "$.store.book[2].price". Can read and set values in parsed JSON objects. homepage: "https://github.com/f3ath/jessie" diff --git a/test/set_test.dart b/test/set_test.dart index c10b326..3547c00 100644 --- a/test/set_test.dart +++ b/test/set_test.dart @@ -25,11 +25,11 @@ void main() { expect(json['store']['bicycle']['color'], 'red', reason: 'Original bike is still red'); }); - test('Does not set non-existing field', () { - final foo = JsonPath(r'$.foo'); - final mutated = foo.set(json, 42); - expect(mutated.containsKey('foo'), false); - expect(mutated['store'], json['store']); + test('Creates a field if it does not exist', () { + final abc = JsonPath(r'$.a.b.c'); + final mutated = abc.set({'foo': 'bar'}, 'magic'); + expect(mutated['foo'], 'bar'); + expect(mutated['a']['b']['c'], 'magic'); }); test('Does not change non-object (scalars, arrays)', () { expect(store.set(42, 'foo'), 42); @@ -97,5 +97,9 @@ void main() { expect(mutated['store']['book'][2], 'banana'); expect(mutated['store']['book'][3]['price'], isA()); }); + test('Index. Setting non-existing index throws RangeError', () { + final title = JsonPath(r'$.store.book[100].title'); + expect(() => title.set(json, 'Banana'), throwsRangeError); + }); }); }