12

I have the following inputs - 2 json files one is the base one and the second contains the same properties but the different values, I'd like to merge that objects.

For example:

{
  a:{
    b:"asda"
  }
  c: "asdasd"
}

And the second file:

{
  a:{
   b:"d"
  }
}

And the result should be like this:

{a:{b:"d"},c:"asdasd"}

Is that is possible with powershell?

Ihor Korotenko
  • 846
  • 1
  • 13
  • 29
  • 2
    FYI, I had to add a comma after the first JSON a: element closing bracket to make it valid JSON. – Mark Wragg Aug 07 '17 at 15:09
  • You might use the [`Merge()` method](https://www.newtonsoft.com/json/help/html/MergeJson.htm) of the [`Newtonsoft.Json.Linq` Namespace](https://www.newtonsoft.com/json/help/html/n_newtonsoft_json_linq.htm) for this, see: [Combine / Merge two json files](https://stackoverflow.com/a/75529898/1701026) – iRon Feb 22 '23 at 09:42

3 Answers3

12

Join (Join-Object) is not a built-in CmdLet

This is an extension of @Mark's answer which also recurses through child objects.

function merge ($target, $source) {
    $source.psobject.Properties | % {
        if ($_.TypeNameOfValue -eq 'System.Management.Automation.PSCustomObject' -and $target."$($_.Name)" ) {
            merge $target."$($_.Name)" $_.Value
        }
        else {
            $target | Add-Member -MemberType $_.MemberType -Name $_.Name -Value $_.Value -Force
        }
    }
}

merge $Json1 $Json2

$Json1
waxingsatirical
  • 584
  • 1
  • 7
  • 11
  • Is there a way to keep the order of properties in target? When merging, I've notice that the properties are replaced towards the bottom of the object. – Jason Foglia Apr 04 '23 at 18:35
  • I actually added another condition to the else portion of the code to set a value instead of adding a member. That seems to have done the trick. – Jason Foglia Apr 05 '23 at 16:24
9

$Json1 | Join $Json2 -Merge {$Right.$_} | ConvertTo-Json (see update below)

Install-Module -Name JoinModule
($Json1 ConvertFrom-Json) | Merge ($Json2 ConvertFrom-Json) | ConvertTo-Json

Result:

{
    "c":  "asdasd",
    "a":  {
              "b":  "d"
          }
}

You might consider not to overwrite the left value:

($Json1 ConvertFrom-Json) | Join ($Json2 ConvertFrom-Json) | ConvertTo-Json

In that case the result will be:

{
    "c":  "asdasd",
    "a":  [
              {
                  "b":  "asda"
              },
              {
                  "b":  "d"
              }
          ]
}

For details see: https://stackoverflow.com/a/45483110/1701026


Update 2019-11-16
The -Merge parameter has been depleted and divided over the -Discern and -Property parameters (sorry for the breaking change). The good news is that the default parameter settings for an object merge are accommodated in a proxy command named Merge-Object (alias Merge) which simplifies the concerned syntax to just: $Object1 | Merge $Object2. For details, see readme or embedded help.

iRon
  • 20,463
  • 10
  • 53
  • 79
4

If you know the names of the elements (per your example above), you could do it explicitly like this:

$Json1 ='{
  a: {
    b:"asda"
  },
  c: "asdasd"
}
' | ConvertFrom-Json

$Json2 = '{
  a:{
   b:"d"
  }
}
' | ConvertFrom-Json


$Json1.a = $Json2.a

Result:

$Json1 | ConvertTo-Json

{
    "a":  {
              "b":  "d"
          },
    "c":  "asdasd"
}

If you're looking for something that will merge the two without knowing the explicit key name, you could do something like the following. This will essentially overwrite any properties in the first Json with those from the second Json, where they are duplicated at the first level (it won't seek matches in the nested properties and again this is an overwrite not a merge):

$Json2.psobject.Properties | ForEach-Object {
    $Json1 | Add-Member -MemberType $_.MemberType -Name $_.Name -Value $_.Value -Force
}
Mark Wragg
  • 22,105
  • 7
  • 39
  • 68
  • yup I'm looking for solution that works based on matching keynames – Ihor Korotenko Aug 07 '17 at 15:31
  • I've amended my answer with another option to overwrite the properties of Json1 with those in Json2, but bear in mind this still isn't a true merge, but maybe gets you a little closer. – Mark Wragg Aug 07 '17 at 16:27