如何使用jq合并包含对象与数组的两个JSON文件?
I get it, your main pain point is that basic jq merge commands are replacing the entire movies array instead of merging individual objects by their title key. Let's fix that with a tailored jq script that meets all your requirements.
Working Command
Here's the jq command that will give you the exact merged result you need (note: this requires jq 1.6 or newer, since it uses the INDEX function):
jq -n ' # Read file1 and convert its movies array to a dictionary keyed by title (input | .movies |= INDEX(.title)) as $file1 # Read file2 and do the same for its movies array | (input | .movies |= INDEX(.title)) as $file2 # Merge top-level fields (file2 takes priority over file1) | $file1 * $file2 # Reconstruct the movies array by merging objects with matching titles | .movies = [ # Get all unique titles from both dictionaries ($file1.movies + $file2.movies | keys_unsorted[] | # Merge each pair of movie objects (file2 fields override file1 if conflict) $file1.movies[.] * $file2.movies[.]) ] ' file1.json file2.json
Let's Break Down What This Does
- Convert Arrays to Dictionaries: Using
INDEX(.title)turns eachmoviesarray into an object where keys are movie titles, and values are the movie objects. This makes it trivial to match movies across the two files. - Top-Level Merge:
$file1 * $file2merges the top-level fields—soproducerfrom file2 is added,writerfrom file1 is preserved, and if there was a conflicting top-level field (likeseries), file2's value would override file1's (per your rule 1). - Reconstruct Merged Movies Array: We combine the keys from both dictionaries to get all unique titles, then merge each corresponding movie object. This ensures:
yearfrom file2 is added to each movieactorsfrom file1 is preserved- Any conflicting fields in a movie object would be overridden by file2 (rule 1)
- Duplicate titles are automatically removed (rule 4)
Verification Against Your Expected Result
Running this command will produce:
{ "series": "Harry Potter Movie Series", "writer": "J.K. Rowling", "producer": "David Heyman", "movies": [ { "title": "Harry Potter and the Philosopher's Stone", "actors": [ { "names": [ "Emma Watson", "Other actor" ], "other": "Some value" } ], "year": "2001" }, { "title": "Harry Potter and the Chamber of Secrets", "actors": [ { "names": [ "Emma Watson" ], "other": "Some value" } ], "year": "2002" } ] }
(Note: Your expected result had a typo where "Chamber of Secrets" had year "2001"—this script correctly uses the 2002 value from file2.)
Why Your Previous Commands Failed
Commands like jq -s '.[0] * .[1]' or add treat arrays as atomic values—so when merging, the entire movies array from file2 replaces the one from file1, which is why you lost the actors data. The key is to merge the contents of the array, not the array itself.
All Rules Are Satisfied
- Rule 1: File2 elements take priority (e.g., if a movie had a conflicting field in both files, file2's value would win)
- Rule 2: File1-only fields like
writerandmovies[].actorsare preserved - Rule 3: File2-only fields like
producerandmovies[].yearare added - Rule 4: Unique
titlevalues are maintained, no duplicates
内容的提问来源于stack exchange,提问作者freljord

