springstil
Goto Top

Csv in XML mit Powershell

Hallo zusammen,

was ich mittlerweile einigermaßen kann, ist es XML in CSV umzuwandeln mit Hilfe von PowerShell face-smile

Mir ist auch schon bei einer anderen CSV zu XML geholfen worden, allerdings komme ich hier gerade nicht wirklich weiter da ich nicht genau weiß wie ich mein Bestehendes Skript ändern solle.

Das Beispiel sieht so aus:

index

Mir geht es um die Felder:

GTIN
Channel SKU
INV_COUNT
QUANTITY_UNIT
DATETIME
DATE (nur wenn INV_COUNT auf 0 ist wenn sowas geht / oder das Feld gefüllt ist)


Ich hoffe mir kann bei dem Skript jemand helfen

Content-Key: 665676

Url: https://administrator.de/contentid/665676

Printed on: April 25, 2024 at 13:04 o'clock

Member: H41mSh1C0R
H41mSh1C0R Apr 13, 2021 at 08:35:21 (UTC)
Goto Top
Aloa,
bei welchem Script, ich seh keines.
VG
Member: Springstil
Springstil Apr 13, 2021, updated at Apr 15, 2021 at 11:43:40 (UTC)
Goto Top
Ich hab das Skript etwas umgeschrieben. Klappt aber auch nur Fast :/

$files = ls '\\10.173.45.12\sog\Bestandsupload\klingel\' -File -Filter *.csv  
$ns = ""  

foreach($file in $files){
    $xml = [xml]'<?xml version="1.0" encoding="utf-8" ?><INVRPT xmlns="http://www.kmopartnerb2b.com" xmlns:xs="http://www.w3.org/2001/XMLSchema" />'  
    $lkz = $xml.CreateElement("Supplier_ID")  
    $inventories = $xml.CreateElement("Inventories")  
    $inventories = $xml.CreateElement("Inventory_Update")  
    $lkz.InnerText = "1234567"  
    $xml.DocumentElement.AppendChild($lkz) | out-null
    
    Import-CSV $file.FullName -Delimiter ";" | %{  
        $inventoryupdate = $xml.CreateElement("Items")  
        $Item = $xml.CreateElement("Item")  
        $SUPPLIER_ID = $xml.CreateElement("SUPPLIER_ID",$ns)  
        $SUPPLIER_ID.InnerText = $_.SUPPLIER_ID
        $inventoryupdate.AppendChild($SUPPLIER_ID)
        $CHANNEL_SKU = $xml.CreateElement("CHANNEL_SKU",$ns)  
        $CHANNEL_SKU.InnerText = $_.CHANNEL_SKU
        $inventoryupdate.AppendChild($CHANNEL_SKU)
        $INV_COUNT = $xml.CreateElement("INV_COUNT",$ns)  
        $INV_COUNT.InnerText = $_.INV_COUNT
        $inventoryupdate.AppendChild($INV_COUNT)                
        $QUANTITY_UNIT = $xml.CreateElement("QUANTITY_UNIT",$ns)  
        $QUANTITY_UNIT.InnerText = $_.QUANTITY_UNIT
        $inventoryupdate.AppendChild($QUANTITY_UNIT)       
        $Datetimes = $xml.CreateElement("Datetime",$ns)  
        $Date = $xml.CreateElement("Date",$ns)  
        $Date.innerText = $_.Date
        $Datetimes.AppendChild($Date)
        $time = $xml.CreateElement("time",$ns)  
        $time.innerText = $_.time
        $Datetimes.AppendChild($time)
         $inventoryupdate.AppendChild($Datetimes)

        $TERM = $xml.CreateElement("TERM",$ns)  
        $TERM.InnerText = $_.TERM
        $inventoryupdate.AppendChild($TERM)  
       
        $inventories.AppendChild($inventoryupdate)
    } | out-null
    $xml.DocumentElement.AppendChild($inventories)
    $outpath = join-path $file.DirectoryName "test.xml"  
    $xml.Save($outpath)
}

So sieht es gerade aus. Allerdings sieht das Ergebniss so aus:

index

Mein Problem ist zum einen das er bei
<Supplier_ID xmlns="">1234567</Supplier_ID>
- <Inventory_Update xmlns="">
das xmlns anzeigt, und zum anderen

ist es möglich, <TERM>123123</TERM> nur zu füllen wenn es auch ein Inhalt hat ?

Meine CSV dazu:

SUPPLIER_ID;INV_COUNT;QUANTITY_UNIT;DATE;TIME;LIEFERTAG;CHANNEL_SKU;Term
3320676-50;325;1;04.04.2020;19:45;20.12.2022;AAA;123123
3320677-50;70;1;04.04.2020;19:45;20.12.2022;AAA;123123
3320679-40;0;1;04.04.2020;19:45;20.12.2022;AAA;123123
3320680-50;202;1;04.04.2020;19:45;20.12.2022;AAA;123123
3320681-50;119;1;04.04.2020;19:45;20.12.2022;AAA;123123
3320682-50;71;1;04.04.2020;19:45;20.12.2022;AAA;123123
3320350-50;17;1;04.04.2020;19:45;20.12.2022;AAA;123123
3320351-50;0;1;04.04.2020;19:45;20.12.2022;AAA;123123
3320177-50;0;1;04.04.2020;19:45;20.12.2022;AAA;123123
Member: colinardo
Solution colinardo Apr 14, 2021 updated at 15:55:39 (UTC)
Goto Top
Servus @Springstil,
habe dir alle Zeilen diesmal noch ausführlich kommentiert, damit nach dem Lesen das Verfahren nun klar geworden sein sollte und du in Zukunft bei neuen Aufgaben darauf aufbauen kannst (hoffentlich face-wink).
Das Prinzip ist eigentlich immer gleich. Und zwar musst du die Knoten einerseits alle mit dem xmlns Namepace der im Document-Root definiert ist erstellen (dazu war die Variable $ns im Kopf gedacht).
Das was du auch verstehen musst, ist die Reihenfolge in der du die Knoten einhängen musst. Dies geschieht immer vom am tiefsten verschachtelten Knoten aus gesehen nach oben. die Kinder werden dann in den nächst höheren Knoten "eingehängt" danach wird dieser Knoten wieder an seinem "Parent" eingehängt, bis man am Document(Root)-Knoten des Trees angekommen ist. Hier im Beispiel sind also die letzten Knoten die man einhängt der INVENTORIES Knoten und der SUPPLIER_ID Knoten die beide sogenannte "Sibblings"(benachbarte Knoten) im Document-Knoten darstellen.

Außerdem solltest du beachten das XML Dokumente Case-Sensitive sind, also ein Element mit dem Namen "Supplier_Id" ist nicht gleich "SUPPLIER_ID", das sind in XML-Syntax zwei gänzlich unterschiedliche Elemente.
# CSV Dateien in Array lesen
$files = ls '\\10.173.45.12\sog\Bestandsupload\klingel' -File -Filter *.csv  
# Default Namespace für Elemente definieren == der Wert der "xmlns"-Property des INVRPT Knotens  
# Alle Knoten die diesen Namespace erben sollen müssen mit diesem Namespace als zweitem Parameter erstellt werden
$ns = "http://www.kmopartnerb2b.com"  

foreach($file in $files){
    $xml = [xml]'<?xml version="1.0" encoding="utf-8" ?><INVRPT xmlns="http://www.kmopartnerb2b.com" xmlns:xs="http://www.w3.org/2001/XMLSchema" />'  
    # SUPPLIER_ID -Knoten erstellen
    $supplier_id = $xml.CreateElement("SUPPLIER_ID",$ns)  
    # und festen Wert zuweisen
    $supplier_id.innerText = '123456'  
    # INVENTORIES -Knoten erstellen
    $inventories = $xml.CreateElement("INVENTORIES",$ns)  
    # INVENTORY_UPDATE -Knoten erstellen
    $inventory_update = $xml.CreateElement("INVENTORY_UPDATE",$ns)  
   
    # CSV einlesen und aus den Zeilen ITEM-Knoten erstellen
    Import-CSV $file.FullName -Delimiter ";" | %{  
        write-host "Verarbeite '$($file.Fullname)' ..."  
        # ITEM-Knoten erstellen
        $itm = $xml.CreateElement("ITEM",$ns)  
        
        # SUPPLIER_SKU_ID-Knoten erstellen
        $SUPPLIER_SKU_ID = $xml.CreateElement("SUPPLIER_SKU_ID",$ns)  
        $SUPPLIER_SKU_ID.InnerText = $_.SUPPLIER_ID
        $itm.AppendChild($SUPPLIER_ID)

        # CHANNEL_SKU-Knoten erstellen
        $CHANNEL_SKU = $xml.CreateElement("CHANNEL_SKU",$ns)  
        $CHANNEL_SKU.InnerText = $_.CHANNEL_SKU
        $itm.AppendChild($CHANNEL_SKU)

        # INV_COUNT-Knoten erstellen
        $INV_COUNT = $xml.CreateElement("INV_COUNT",$ns)  
        $INV_COUNT.InnerText = $_.INV_COUNT
        $itm.AppendChild($INV_COUNT)

        # QUANTITY_UNIT-Knoten erstellen
        $QUANTITY_UNIT = $xml.CreateElement("QUANTITY_UNIT",$ns)  
        $QUANTITY_UNIT.InnerText = $_.QUANTITY_UNIT
        $itm.AppendChild($QUANTITY_UNIT)
        
        # wenn INV_COUNT Spalte der CSV nicht leer DATETIME Knoten erstellen
        if ($_.INV_COUNT -ne ''){  
            $Datetimes = $xml.CreateElement("DATETIME",$ns)  
            $Date = $xml.CreateElement("DATE",$ns)  
            $Date.innerText = $_.Date
            $Datetimes.AppendChild($Date)

            $time = $xml.CreateElement("TIME",$ns)  
            $time.innerText = $_.time
            $Datetimes.AppendChild($time)
            $itm.AppendChild($Datetimes)
        }
        # Wenn TERM Spalte der CSV nicht leer ist TERM Knoten hinzufügen
        if ($_.Term -ne ''){  
            $TERM = $xml.CreateElement("TERM",$ns)  
            $TERM.InnerText = $_.TERM
            $itm.AppendChild($TERM)
        }

        # ITEM-Knoten in INVENTORY_UPDATE-Knoten anhängen
        $inventory_update.AppendChild($itm) | out-null
    } | out-null

    # INVENTORY_UPDATE Knoten im INVENTORIES Knoten einhängen
    $inventories.AppendChild($inventory_update) | out-null
    # SUPPLIER_ID Knoten in den Root-Knoten einhängen
    $xml.DocumentElement.AppendChild($supplier_id) | out-null
    # Abschließend INVENTORIES Knoten mit in den Root-Knoten einhängen
    $xml.DocumentElement.AppendChild($inventories) | out-null
    # Ausgabepfad erstellen
    $outpath = join-path $file.DirectoryName ($file.Basename + ".xml")  
    # XML Dokument  im Ausgabepfad speichern
    $xml.Save($outpath)
}

Ich würde dir auch mal ans Herz legen dich in die Grundlagen von XML Dokumenten einzuarbeiten dann verstehst du das mit den Namespaces auch besser. Auch wenn die Materie trocken anmutet, du wirst sehen sie hilft dir jedes mal wenn du mit XML-Dateien hantieren musst. Und so viel gibt es da auch nicht zu verstehen.
https://www.data2type.de/xml-xslt-xslfo/xml/xml-in-a-nutshell/xml-grundl ...
Zusätzlich die Dokumentation zum XMLDocument-Object seinen Methoden und Eigenschaften auch ganz hilfreich für die Powershell da das die selben Objekte sind
https://docs.microsoft.com/de-de/dotnet/api/system.xml.xmldocument?view= ...

Hope this helps
Viel Erfolg.

Grüße Uwe
Member: Springstil
Springstil Apr 15, 2021 at 13:59:23 (UTC)
Goto Top
Vielen dank für die Lösung ich arbeite grade beim Nächsten Kunden an so einer Konvertierung und komme gut voran dank der guten Erklärung face-smile

Ich habe die Datei dann zu dem Kunden geschickt und bekomme als Antwort:

leider sind noch Fehler in der XML. So wie es aussieht auf der Stelle 1 in der ersten Zeile x‘239‘


Ich kann nicht ganz nachvollziehen was die damit meinen... Im Online Hex Editor
https://prnt.sc/11g6tpc

ist dieses Leerzeichen auch zu sehen. Nur wie kommt das bitte da rein? wenn ich es mit Notepad oder so öffne, ist es nicht zu sehen.
Member: colinardo
colinardo Apr 15, 2021 updated at 14:23:19 (UTC)
Goto Top
Zitat von @Springstil:
leider sind noch Fehler in der XML. So wie es aussieht auf der Stelle 1 in der ersten Zeile x‘239‘


Ich kann nicht ganz nachvollziehen was die damit meinen... Im Online Hex Editor
https://prnt.sc/11g6tpc

Bitte in Zukunft keine externen Bilder Links mehr! Du kannst Bilder hier im Forum hochladen, siehe Links ganz unten das Bildchen.
ist dieses Leerzeichen auch zu sehen. Nur wie kommt das bitte da rein? wenn ich es mit Notepad oder so öffne, ist es nicht zu sehen.
Das ist kein Leerzeichen das ist der reguläre Byte Order Mark EF BB BF Prefix einer UTF8 Datei, dieser ist vollkommen legitim.
https://de.wikipedia.org/wiki/Byte_Order_Mark

G. Uwe
Member: Springstil
Springstil Apr 15, 2021 updated at 14:29:03 (UTC)
Goto Top
Der Fehlercode vom System vom Kunden lautet:
Error in line 1: XML file should begin with white spaces or <. (Found char :ï(Hex :239).

wenn ich mir den inhalt neu kopiere in eine Text Datei, habe ich im Hex Editor kein Byte Order Mark mehr drin. Kann ich das Problem irgendwie umgehen?

EDIT: Scheint daran zulegen das er nicht in UTF8 schreibt sondern in UTF8-BOM. Wie kann ich das denn beeinflussen???
Member: colinardo
colinardo Apr 15, 2021 updated at 14:35:37 (UTC)
Goto Top
Zitat von @Springstil:

Der Fehlercode vom System vom Kunden lautet:
Error in line 1: XML file should begin with white spaces or <. (Found char :ï(Hex :239).

wenn ich mir den inhalt neu kopiere in eine Text Datei, habe ich im Hex Editor kein Byte Order Mark mehr drin. Kann ich das Problem irgendwie umgehen?

EDIT: Scheint daran zulegen das er nicht in UTF8 schreibt sondern in UTF8-BOM. Wie kann ich das denn beeinflussen???
Dann ist deren System wohl etwas unflexibel, indem Fall einfach die XML ohne BOM speichern face-smile Speicherprozdur in Zeile 76 austauschen durch
    # XML Dokument im Ausgabepfad speichern
    $writer = New-Object System.Xml.XmlTextWriter $outpath, (New-Object System.Text.UTF8Encoding $false)
    $writer.Formatting = 'Indented'  
    $xml.Save($writer)
    $writer.Close()
Grüße Uwe
Member: Springstil
Springstil Apr 15, 2021 at 14:36:20 (UTC)
Goto Top
Vielen Vielen Dank! Damit war ich auch gerade am Experimentieren :D

Ja die sind sehr Unflexibel.. Da gibt es noch einige große Versandhäuser die ziemlich steif sind.
Member: Springstil
Springstil Jun 15, 2021 at 09:20:57 (UTC)
Goto Top
Hey face-smile ich habe ein Kleines Problem mit dem Skript. Verstehe aber nicht ganz wieso das passiert. Und zwar steht in der CSV die Zeit mit 01:36:28 drin. In der XML kommt dies aber ohne die Führende "0" raus. Sprich dort steht dann: 1:36:28

Wie kann ich also das Format ändern?
Member: colinardo
colinardo Jun 15, 2021 updated at 10:36:43 (UTC)
Goto Top
Servus nochmal.
Zitat von @Springstil:
Hey face-smile ich habe ein Kleines Problem mit dem Skript. Verstehe aber nicht ganz wieso das passiert. Und zwar steht in der CSV die Zeit mit 01:36:28 drin. In der XML kommt dies aber ohne die Führende "0" raus. Sprich dort steht dann: 1:36:28
Kann ich hier im Test nicht nachvollziehen denn der Code ändert rein gar nichts an den Quelldaten, die wandern von der CSV so wies sie sind in die XML. Das wird bei dir zu 99% ein Anzeigefehler der XML-Datei sein.
Wie kann ich also das Format ändern?
Wenn die Daten aber unterschiedlich z,B, (ohne führende Nullen) vorliegen kannst du sie in folgender Zeile so vereinheitlichen
$time.innerText = ([timespan]$_.time).ToString('t')  
Grüße Uwe
Member: Springstil
Springstil Jun 15, 2021 at 11:11:37 (UTC)
Goto Top
ach... Sorry... Darf ich das auf das Wetter schieben? Wenn ich die CSV öffne zeigt er es richtig aber unsere Wawi schreibt in der Ansicht die null mit rein, gibt sie aber nicht aus. Wenn ich es mit Notepad++ öffne fehlt die null