Jooyung Han | 04329f1 | 2019-08-01 23:35:08 +0900 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # |
| 3 | # Copyright (C) 2019 The Android Open Source Project |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
| 16 | |
| 17 | |
| 18 | import argparse |
| 19 | import collections |
| 20 | import json |
| 21 | import sys |
| 22 | |
| 23 | def follow_path(obj, path): |
| 24 | cur = obj |
| 25 | last_key = None |
| 26 | for key in path.split('.'): |
| 27 | if last_key: |
| 28 | if last_key not in cur: |
| 29 | return None,None |
| 30 | cur = cur[last_key] |
| 31 | last_key = key |
| 32 | if last_key not in cur: |
| 33 | return None,None |
| 34 | return cur, last_key |
| 35 | |
| 36 | |
| 37 | def ensure_path(obj, path): |
| 38 | cur = obj |
| 39 | last_key = None |
| 40 | for key in path.split('.'): |
| 41 | if last_key: |
| 42 | if last_key not in cur: |
| 43 | cur[last_key] = dict() |
| 44 | cur = cur[last_key] |
| 45 | last_key = key |
| 46 | return cur, last_key |
| 47 | |
| 48 | |
| 49 | class SetValue(str): |
| 50 | def apply(self, obj, val): |
| 51 | cur, key = ensure_path(obj, self) |
| 52 | cur[key] = val |
| 53 | |
| 54 | |
| 55 | class Replace(str): |
| 56 | def apply(self, obj, val): |
| 57 | cur, key = follow_path(obj, self) |
| 58 | if cur: |
| 59 | cur[key] = val |
| 60 | |
| 61 | |
| 62 | class Remove(str): |
| 63 | def apply(self, obj): |
| 64 | cur, key = follow_path(obj, self) |
| 65 | if cur: |
| 66 | del cur[key] |
| 67 | |
| 68 | |
| 69 | class AppendList(str): |
| 70 | def apply(self, obj, *args): |
| 71 | cur, key = ensure_path(obj, self) |
| 72 | if key not in cur: |
| 73 | cur[key] = list() |
| 74 | if not isinstance(cur[key], list): |
| 75 | raise ValueError(self + " should be a array.") |
| 76 | cur[key].extend(args) |
| 77 | |
| 78 | |
| 79 | def main(): |
| 80 | parser = argparse.ArgumentParser() |
| 81 | parser.add_argument('-o', '--out', |
| 82 | help='write result to a file. If omitted, print to stdout', |
| 83 | metavar='output', |
| 84 | action='store') |
| 85 | parser.add_argument('input', nargs='?', help='JSON file') |
| 86 | parser.add_argument("-v", "--value", type=SetValue, |
| 87 | help='set value of the key specified by path. If path doesn\'t exist, creates new one.', |
| 88 | metavar=('path', 'value'), |
| 89 | nargs=2, dest='patch', default=[], action='append') |
| 90 | parser.add_argument("-s", "--replace", type=Replace, |
| 91 | help='replace value of the key specified by path. If path doesn\'t exist, no op.', |
| 92 | metavar=('path', 'value'), |
| 93 | nargs=2, dest='patch', action='append') |
| 94 | parser.add_argument("-r", "--remove", type=Remove, |
| 95 | help='remove the key specified by path. If path doesn\'t exist, no op.', |
| 96 | metavar='path', |
| 97 | nargs=1, dest='patch', action='append') |
| 98 | parser.add_argument("-a", "--append_list", type=AppendList, |
| 99 | help='append values to the list specified by path. If path doesn\'t exist, creates new list for it.', |
| 100 | metavar=('path', 'value'), |
| 101 | nargs='+', dest='patch', default=[], action='append') |
| 102 | args = parser.parse_args() |
| 103 | |
| 104 | if args.input: |
| 105 | with open(args.input) as f: |
| 106 | obj = json.load(f, object_pairs_hook=collections.OrderedDict) |
| 107 | else: |
| 108 | obj = json.load(sys.stdin, object_pairs_hook=collections.OrderedDict) |
| 109 | |
| 110 | for p in args.patch: |
| 111 | p[0].apply(obj, *p[1:]) |
| 112 | |
| 113 | if args.out: |
| 114 | with open(args.out, "w") as f: |
Martin Stjernholm | 37fa32c | 2020-02-24 15:52:31 +0000 | [diff] [blame] | 115 | json.dump(obj, f, indent=2, separators=(',', ': ')) |
| 116 | f.write('\n') |
Jooyung Han | 04329f1 | 2019-08-01 23:35:08 +0900 | [diff] [blame] | 117 | else: |
Martin Stjernholm | 37fa32c | 2020-02-24 15:52:31 +0000 | [diff] [blame] | 118 | print(json.dumps(obj, indent=2, separators=(',', ': '))) |
Jooyung Han | 04329f1 | 2019-08-01 23:35:08 +0900 | [diff] [blame] | 119 | |
| 120 | |
| 121 | if __name__ == '__main__': |
| 122 | main() |