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 18import argparse 19import collections 20import json 21import sys 22 23def 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 37def 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 49class SetValue(str): 50 def apply(self, obj, val): 51 cur, key = ensure_path(obj, self) 52 cur[key] = val 53 54 55class Replace(str): 56 def apply(self, obj, val): 57 cur, key = follow_path(obj, self) 58 if cur: 59 cur[key] = val 60 61 62class Remove(str): 63 def apply(self, obj): 64 cur, key = follow_path(obj, self) 65 if cur: 66 del cur[key] 67 68 69class 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 79def 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: 115 json.dump(obj, f, indent=2, separators=(',', ': ')) 116 f.write('\n') 117 else: 118 print(json.dumps(obj, indent=2, separators=(',', ': '))) 119 120 121if __name__ == '__main__': 122 main() 123