The open source OpenXR runtime
1#!/usr/bin/env python3
2# Copyright 2025, NVIDIA CORPORATION.
3# SPDX-License-Identifier: BSL-1.0
4"""
5Helper script to merge multiple JSON files into one.
6
7This script merges the root JSON objects from multiple input files
8while preserving field ordering. Files are processed in sorted order
9to ensure consistent results. Fields are merged in the order they
10appear across the input files.
11"""
12
13import argparse
14import json
15import sys
16from pathlib import Path
17
18
19def merge_json_files(json_files, output_file, allow_overwrite=False,
20 ignore_schema=False):
21 """
22 Merge multiple JSON files into a single output file.
23
24 Args:
25 json_files: List of paths to JSON files to merge
26 output_file: Path to the output file
27 allow_overwrite: If True, allow later files to overwrite keys
28 from earlier files
29 ignore_schema: If True, ignore the "$schema" field in input files
30 """
31 merged = {}
32
33 # Sort files to ensure consistent ordering
34 sorted_files = sorted(json_files)
35
36 for json_file in sorted_files:
37 try:
38 with open(json_file, 'r', encoding='utf-8') as f:
39 data = json.load(f)
40
41 if not isinstance(data, dict):
42 print(f"Error: {json_file} does not contain a JSON "
43 f"object at root level",
44 file=sys.stderr)
45 sys.exit(1)
46
47 # Merge while preserving order - check for duplicates
48 for key, value in data.items():
49 if key == "$schema" and ignore_schema:
50 continue
51 if key in merged:
52 if allow_overwrite:
53 print(f"Warning: Key '{key}' from {json_file} "
54 f"overwrites previous definition",
55 file=sys.stderr)
56 else:
57 print(f"Error: Duplicate key '{key}' found in "
58 f"{json_file}. Use --allow-overwrite to "
59 f"permit overwriting.",
60 file=sys.stderr)
61 sys.exit(1)
62 merged[key] = value
63
64 except FileNotFoundError:
65 print(f"Error: File not found: {json_file}", file=sys.stderr)
66 sys.exit(1)
67 except json.JSONDecodeError as e:
68 print(f"Error: Failed to parse JSON from {json_file}: {e}",
69 file=sys.stderr)
70 sys.exit(1)
71 except Exception as e:
72 print(f"Error: Failed to read {json_file}: {e}",
73 file=sys.stderr)
74 sys.exit(1)
75
76
77 # Write merged JSON to output file
78 try:
79 with open(output_file, 'w', encoding='utf-8') as f:
80 json.dump(merged, f, indent='\t', ensure_ascii=False)
81 f.write('\n') # Add trailing newline
82
83 # Printing, left in if we want it in the future.
84 if False:
85 print(f"Successfully merged {len(json_files)} file(s) into "
86 f"{output_file}")
87 except Exception as e:
88 print(f"Error: Failed to write to {output_file}: {e}",
89 file=sys.stderr)
90 sys.exit(1)
91
92
93def main():
94 parser = argparse.ArgumentParser(
95 description='Merge multiple JSON files into one, preserving '
96 'field ordering.',
97 formatter_class=argparse.RawDescriptionHelpFormatter,
98 epilog="""
99Examples:
100 %(prog)s -o merged.json file1.json file2.json file3.json
101 %(prog)s -o output.json base.json overrides.json
102 %(prog)s -o output.json --allow-overwrite base.json overrides.json
103 %(prog)s -o output.json --ignore-schema schema.json data.json
104 """
105 )
106
107 parser.add_argument('-o', '--output',
108 required=True,
109 metavar='OUTPUT',
110 help='Output file path for the merged JSON')
111
112 parser.add_argument('--allow-overwrite',
113 action='store_true',
114 help='Allow later files to overwrite duplicate '
115 'keys from earlier files')
116
117 parser.add_argument('--ignore-schema',
118 action='store_true',
119 help='Ignore the "$schema" field in input files')
120
121 parser.add_argument('json_files',
122 nargs='+',
123 metavar='JSON_FILE',
124 help='JSON files (will be sorted alphabetically)')
125
126 args = parser.parse_args()
127
128 # Validate that we have at least one input file
129 if not args.json_files:
130 print("Error: At least one JSON file must be provided",
131 file=sys.stderr)
132 sys.exit(1)
133
134 merge_json_files(args.json_files, args.output, args.allow_overwrite,
135 args.ignore_schema)
136
137
138if __name__ == '__main__':
139 main()