none
Powershell CustumObject mergen RRS feed

  • Frage

  • Hi, hab eine Frage vielleicht hat jemand eine Idee.

    Ich lese zwei JSON files ein und würde gerne die Informationen in ein PSCustomObject zusammenführen allerdings sollen immer nur die einzelnen werte verändert werden falls die Information schon vorhanden ist .

    $Json ist dabei eine globale Information und die Werte von $Json2 haben das letzte Wort sollte es zu gleichen Key/Value pairs kommen.

    Die Information aus der $Json2 ist also führend, soll aber nur Informationen überschreiben oder anreichern.

    Beispiel der JSONs.

    $Json1:

    {

       "Hostname": "Server1",

       "Port": ""5001",

       "Pfade":{

                   "Pfad1": "C:\\test\123",

                   "Pfad2": "C:\\test\456"

                   "Pfad3": "C:\\test\456"

                   "Pfad4": "C:\\test\456"

    }

    }

    $Json2:

    {

       "Hostname": "Server2",

       "Port": ""5001",

       "Pfade":{

                   "Pfad1": "E:\\Prod\123",

                   "Pfad2": "E:\\Prod\456"

    }

    }

    Ich lese die beiden Dateien und konvertiere mittels ConvertFrom-Json

    Die Informationen stehen jetzt also in den Variablen $Json1 und $Json2

    Ich merge die beiden variablen nun zu einem Objekt.

    $Object = [ordered] @{}
            foreach ($Property in $Json1.PSObject.Properties) {
            $Object += @{$Property.Name = $Property.Value}

        }

        foreach ($Property in $JSON2.PSObject.Properties) {
            try{
            $Object += @{$Property.Name = $Property.Value}
            }catch{
                   $Object.$($Property.Name) =  $Property.Value
                    }
            }
            } 

    Das Ergebnis des neu erstellten Objektes sollte wie folgt aussehen:

     "Hostname": "Server2"

       "Port": ""5001",

      "Pfade":@{

                   "Pfad1": "E:\\Prod\123"

                   "Pfad2": "E:\\Prod\456"

                   "Pfad3": "C:\\test\456"

                   "Pfad4": "C:\\test\456"}

    Was aber rauskommt ist:

    "Hostname": "Server2"

       "Port": ""5001",

      "Pfade":@{

                   "Pfad1": "E:\\Prod\123"

                   "Pfad2": "E:\\Prod\456"

    }

    Der Hostname aus der JSON2 wurde übernommen und der port blieb wie gewünscht unverändert.

    Nur die werte des Pfad1 und Pfad2 wurden mit den informationen der JSON2 verändert, PFad3 und pfad4 aber gelöscht.

    Der Code oben löscht mir aber genau diese Informationen Pfad3 und pfad4. Alles andere wird einwandfrei ersetzt.

    ich habe also Probleme nur die Verschachtelten Werte anzupassen. Alles auf erster Ebene funktioniert, sobald aber eine weitere Ebene hinzukommt, Pfad-> Pfad1 wird die Information von Pfad komplett ersetzt.

     Das ist im code ziemlich klar, allerdings hab ich nicht wirklich die Idee wie ich das Problem lösen könnte und nur die Infos von Pfad1 und 2 anpasse aber nicht Pfad3 und 4($json1) lösche.

    Ergebnis sollte so aussehen.

    Das Ergebnis des neu erstellten Objektes sollte wie folgt aussehen:

     "Hostname": "Server2"

       "Port": ""5001",

      "Pfade":@{

                   "Pfad1": "E:\\Prod\123"

                   "Pfad2": "E:\\Prod\456"

                   "Pfad3": "C:\\test\456"

                   "Pfad4": "C:\\test\456"}

    jemand ne Idee?

    Vielen dank und beste grüße








    • Bearbeitet Rolf Kirsch Dienstag, 1. Dezember 2020 09:34
    Dienstag, 1. Dezember 2020 09:15

Antworten

  • Moin,

    poste bitte Code als Code (2. Button von rechts).

    Ansonsten ist der Fehler ja klar: Du gehst durch die Properties des 2. Objekts (eine davon ist "Pfade") und ersetzt den Wert der gesamten Property (also die Hashtable), falls vorhanden.

    Du müsstest also "Pfade" aus der Betrachtung rausnehmen und dafür eine separate Schleife drehen.

    Ich weiß nicht, was Dein Use Case ist, aber in der Annahme, dass die Daten beliebig sein können, sehe ich eine weitere Gefahr:

    Objekt 1:
    Pfad1 = A
    Pfad2 = B
    Pfad3 = C

    verschmolzen mit

    Objekt 2:
    Pfad1 = D
    Pfad2 = E
    Pfad3 = F
    wird wieder nur die Daten aus dem Objekt 2 zurück liefern. Vielleicht ist der Fall bei Dir ja aus irgendeinem Grund ausgeschlossen, aber zu prüfen wäre, ob Du die Pfade nicht besser als Array löst, ohne ihnen benannte Properties zuzuweisen...


    Evgenij Smirnov

    http://evgenij.smirnov.de

    Dienstag, 1. Dezember 2020 09:39

Alle Antworten

  • Wenn es denn ein PSObjekt ist kannst du Eigenschaften per add-member hinzufügen.
    https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/add-member?view=powershell-7.1

    Auf vorhandene Eigenschaften greifst du direkt zu.

    https://docs.microsoft.com/de-de/powershell/scripting/learn/deep-dives/everything-about-pscustomobject?view=powershell-7.1

    Dienstag, 1. Dezember 2020 09:24
  • Hi, danke für die Info. 

    Ja, allerdings muss ich ja den Namen angeben. 

    Sobald ich das mache überschreibt es die Info <Pfad> komplett oder added unterhalb <Pfad> dann ein weiteres <Pfad>

    "Hostname": "Server2"

       "Port": ""5001",

      "Pfade":@{

                   "Pfad1": "E:\\Prod\123"

                   "Pfad2": "E:\\Prod\456"

                   "Pfad3": "C:\\test\456"

                   "Pfad4": "C:\\test\456"}

                    Pfade":@{"Pfad1":....}

    Auch soll das ganze generisch sein, was bedeutet das die infos aus den Jsons nicht immer die namen verwenden wie im beispiel. Auf das Property Pfad1 kann ich also über $Object.$($Property.Name).Pfad1 nicht zugreifen da es nicht immer gegeben ist das es auch so heißt.


                
    • Bearbeitet Rolf Kirsch Dienstag, 1. Dezember 2020 09:36
    Dienstag, 1. Dezember 2020 09:31
  • Moin,

    poste bitte Code als Code (2. Button von rechts).

    Ansonsten ist der Fehler ja klar: Du gehst durch die Properties des 2. Objekts (eine davon ist "Pfade") und ersetzt den Wert der gesamten Property (also die Hashtable), falls vorhanden.

    Du müsstest also "Pfade" aus der Betrachtung rausnehmen und dafür eine separate Schleife drehen.

    Ich weiß nicht, was Dein Use Case ist, aber in der Annahme, dass die Daten beliebig sein können, sehe ich eine weitere Gefahr:

    Objekt 1:
    Pfad1 = A
    Pfad2 = B
    Pfad3 = C

    verschmolzen mit

    Objekt 2:
    Pfad1 = D
    Pfad2 = E
    Pfad3 = F
    wird wieder nur die Daten aus dem Objekt 2 zurück liefern. Vielleicht ist der Fall bei Dir ja aus irgendeinem Grund ausgeschlossen, aber zu prüfen wäre, ob Du die Pfade nicht besser als Array löst, ohne ihnen benannte Properties zuzuweisen...


    Evgenij Smirnov

    http://evgenij.smirnov.de

    Dienstag, 1. Dezember 2020 09:39
  • Mittels

    $Object += @{$Property.Name = $Property.Value}

    Fügst du dem Objekt eine Hashtable "@{$Property.Name = $Property.Value}" hinzu und keine Eigenschaft.

    $Name = $Property.Name
    $Object.$Name = $Property.Value

    überschreibt die vorhandene Eigenschaft. Wenn es ein PSObject ist, gibts u.U. einen Fehler und du kannst Add-Member verwenden. Ist es eine Hashtable wird der Eintrag hinzugefügt oder überschrieben.

    Schau einfach in die Links;-).

    Dienstag, 1. Dezember 2020 09:41
  • Hi danke ,

    ja, das ich aktuell nur die zweite Ebene bearbeite ist klar und war ursprünglich auch so gewollt.

    Das wenn im Objetkt2 gleiche properties aber mit unterschiedlichen Werten vorhanden sind und dies dann übernommen werden ist gewollt, da das zweite Objekt das bestimmende ist.

    Wäre jetzt im Objekt1 noch ein Pfad4, dann müsste der aber mit übernommen werden.

    Das neue Objekt3 hätte dann alle Infos aus Objekt1 aber mit eventuell angepassten Werten aus Objekt2 oder auch weiteren properties. 

    ich teste es mal mit einer schleife..

    Viele Grüße

    Rolf

    Dienstag, 1. Dezember 2020 09:48
  • hi, ja aber genau das soll doch nicht passieren bzw. nicht auf der Ebene Pfad, da dann alles überschrieben wird.

    Bei Hostname und port (erste Ebene) funktioniert das einwandfrei.

    Der Code baut ein Objekt aus Jason1.

    Anschließend versucht er eventuelle neue Properties aus der Json2 zu adden (try) und falls das property schon existiert nur den Wert zu verändern (catch)

    Das funktioniert auf einer Eben gut, nur eben auf der zweiten Ebene (Pfade->Pfad1 bis X) nicht da ich ja mit dem Code oben das property Pfade komplett ersetzte.

    Ich denke das ich hier um eine Schleife nicht drumherum komme, wie Evgenij geschrieben hatte.

    beste Grüße



    • Bearbeitet Rolf Kirsch Dienstag, 1. Dezember 2020 09:58
    Dienstag, 1. Dezember 2020 09:56

  • ja, das ich aktuell nur die zweite Ebene bearbeite ist klar und war ursprünglich auch so gewollt.

    Nee, Du bearbeitest aktuell nur die ERSTE Ebene, und genau das ist Dein Problem ;-)

    Evgenij Smirnov

    http://evgenij.smirnov.de

    Dienstag, 1. Dezember 2020 10:00
  • Ja sorry...hast recht :) mea culpa

    Und ich weiß grad nicht wie ich sauber an die zweite komme und die erste nicht hinten rüber fällt oder daten aus dem ersten Objekt vollständig gelöscht werden.




    • Bearbeitet Rolf Kirsch Dienstag, 1. Dezember 2020 10:05
    Dienstag, 1. Dezember 2020 10:03
  • Ja sorry...hast recht :) mea culpa

    Und ich weiß grad nicht wie ich sauber an die zweite komme und die erste nicht hinten rüber fällt.



    Naja, auch wenn die Pfade nicht einzeln benamst werden müssen, ist der Name "Pfade" ja fix. Du kannst ihn also explizit ausklammern.

    Und wenn Du die Pfade als Array statt Hashtable speicherst, kannst Du sie mit

    $Obj1.Pfade + $Obj2.Pfade | Select-Object -Unique

    konsolidieren.


    Evgenij Smirnov

    http://evgenij.smirnov.de


    Dienstag, 1. Dezember 2020 10:08
  • Ja sorry...hast recht :) mea culpa

    Und ich weiß grad nicht wie ich sauber an die zweite komme und die erste nicht hinten rüber fällt.



    Naja, auch wenn die Pfade nicht einzeln benamst werden müssen, ist der Name "Pfade" ja fix. Du kannst ihn also explizit ausklammern.

    Und wenn Du die Pfade als Array statt Hashtable speicherst, kannst Du sie mit

    $Obj1.Pfade + $Obj2.Pfade | Select-Object -Unique

    konsolidieren.


    Evgenij Smirnov

    http://evgenij.smirnov.de


    Hi, dabei habe ich aber keine Kontroller das auch wirklich die infos des Obj2 die maßgebenden Informationen sind.



    • Bearbeitet Rolf Kirsch Dienstag, 1. Dezember 2020 10:24
    Dienstag, 1. Dezember 2020 10:24
  • Das stimmt. Dann halt zu Fuß, wenn es ein Requirement ist ;-)

    Evgenij Smirnov

    http://evgenij.smirnov.de

    Dienstag, 1. Dezember 2020 10:31
  • Das stimmt. Dann halt zu Fuß, wenn es ein Requirement ist ;-)

    Evgenij Smirnov

    http://evgenij.smirnov.de

    :D Ja - genau das ist ja mein Problem.

    ich bekomme nur die erste Eben sauber ersetzt, ab der zweiten Ebene wird einfach alles ersetzt. Bei einfachen Werten ist das ok, sobald aber wieder mehrere Werte in der Zweiten Ebene vorhanden sind, werden einfach alle ersetzt. Ich habe aktuell nicht wirklich eine Idee wie ich diese zweite ebene ohne die property namen zu wissen einzeln anpassen kann. Ich müsste eigentlich nicht einmal vergleichen sondern einfach nur die einzelnen Werte ab Ebene zwei durchiterieren und entweder weitere properties hinzufügen (try), falls dieses noch nicht existiert, oder eben nur den wert des existierenden property ändern (catch)....Eigentlich der code oben zweimal....in einer weiteren schleife wie von dir vorgeschlagen....versuch macht klug :)

    danke für das feedback


    • Bearbeitet Rolf Kirsch Dienstag, 1. Dezember 2020 10:40
    Dienstag, 1. Dezember 2020 10:39
  • Oder eine Funktion, der du das Objekt und die Eigenschaftenliste zum mergen übergibst.
    In der Schleife der Funktion kannst du prüfen ob es sich wiederum um ein Objekt handelt und die Funktion rekursiv wieder aufrufen. Da übergibst du dann das untergeordnete  Objekt.

    Oder, wie schon selber festgestellt in einer 2-fachen For-Schleife.


    Dienstag, 1. Dezember 2020 10:43
  • Oder eine Funktion, der du das Objekt und die Eigenschaftenliste zum mergen übergibst.
    In der Schleife der Funktion kannst du prüfen ob es sich wiederum um ein Objekt handelt und die Funktion rekursiv wieder aufrufen. Da übergibst du dann das untergeordnete  Objekt.

    Oder, wie schon selber festgestellt in einer 2-fachen For-Schleife.


    Das Mergen macht ja die Funktion wie oben beschrieben (code). ich veruche mal eine weitere schleife...mal sehen

    danke dir

    Dienstag, 1. Dezember 2020 10:51
  • Falls es jemanden interessiert:

    Ist der erste versuch und schön ist es noch nicht....für ideen bin ich offen...besten dank

    $Object = [ordered] @{}
    
       foreach ($Property in $Json1.PSObject.Properties) {
                $Object += @{$Property.Name = $Property.Value}
             }
    
       foreach ($Property in $Json2.PSObject.Properties) {
            try{
                $Object += @{$Property.Name = $Property.Value}
               }catch{
                      $PropName = $($Property.Name)
               if($Property.TypeNameOfValue -notmatch 'System.String'){
                    foreach ($path in $Object.$PropName.PSObject.Properties){
                            $newVal = $Json2.$PropName.PSObject.Properties | Where-Object {$_.Name -eq $path.Name}
                            if ($newVal) {
                                $path.Value = $newVal.Value
                                }       
                            }
                        }else{$Object.$($Property.Name) =  $Property.Value}
                      }
                }

    Dienstag, 1. Dezember 2020 20:08