0

I am quite new to PowerShell. Currently, I'm struggling with the following setup. There are several XML files contained in this folder structure:

Root
|_2020-XX-XX
   |_file1.xml
   |_file2.xml
|_2020-yy-yy
    |_file1.xml
    |_file2.xml

Now, I want to create a script that merges parts of each folder's xml files into one.

The result should look something like this:

|_2020-XX-XX
   |_file1.xml
   |_file2.xml
   |_newfile2020XXXX.xml
|_2020-yy-yy
    |_file1.xml
    |_file2.xml
    |_newfile2020YYYY.xml

So far, my script looks like this:

$date=Get-Date -Format "yyyyMMdd"
#Get the date for naming the output-file

$xmlFilePath = Get-ChildItem "\\Server\Rootfolder" -Recurse -Force | Sort-Object LastWriteTime
#Get and sort the xml-Files by date

$finalXml = "<root>"

foreach ($file in $_.xmlFilePath) {
    [xml]$xml = Get-Content $file
    $finalXml += $xml.root.InnerXml
}
$finalXml += "</root>"
#XML Object closer

([xml]$finalXml).Save("$pwd\newfile$date.xml")

While the debugger does not produce any error, the created file is

  • neither saved in the correct directory ("$pwd" does not seem to be the correct variable)
  • empty except for the <root></root> nodes.

I can't quite figure out what to do, to troubleshoot this and would be grateful for any advice on this.

Thanks in advance for any hint!

1
  • $_.xmlFilePath -> $xmlFilePath Commented Feb 10, 2020 at 13:45

1 Answer 1

1

You seem to want to create one "merged" file per folder. That means you need two loops - one for the folders, and one for the files per folder.

$date = Get-Date -Format "yyyyMMdd"

Get-ChildItem "\\Server\Rootfolder" -Directory | ForEach-Object {
    $folder = $_.FullName
    Write-Host "Processing $folder..."

    $allData = [xml]"<root></root>"

    Get-ChildItem $folder -Filter "*.xml" | Sort-Object LastWriteTime | ForEach-Object {
        if ($_.Name -like "newfile*") { return }
        Write-Host " -" $_.name

        $xml = New-Object xml
        $xml.Load($_.FullName)

        $imported = $allData.ImportNode($xml.DocumentElement, $true)
        $allData.DocumentElement.AppendChild($imported) | Out-Null
    }

    $finalPath = Join-Path $folder "newfile_$date.xml"
    $allData.Save($finalPath)
}

Apart from that, don't process XML as if it were plain text. Use XmlDocument.Load() and XmlDocument.Save() for reading and writing, and use XmlDocument.ImportNode() to transfer content from one document to another.

These two lines are synonymous for creating an XmlDocument (docs):

$xml = New-Object xml
$xml = New-Object System.Xml.XmlDocument

Doing so guarantees that input files are interpreted correctly for their file encoding ([xml]$xml = Get-Content ... does not guarantee that) and that generated output is proper XML as well (concatenating some strings and writing the result to file does not guarantee that).

It's also a good idea to exclude "merged" files (if ($_.Name -like "newfile*") { return }), so you don't end up adding them to each other recursively.

Sign up to request clarification or add additional context in comments.

10 Comments

@nudZ Good to hear! :) (Don't forget to tick off the answer as accepted when it solves your issue.)
Alright. Just another thing: If I want to select a specific node-range to copy, i.e. the third one until the 5th one, how would I do that?
And additionally, how do I get the <?xml version='1.0' encoding='utf-8'?> declaration?
@nudZ Second question first: The <?xml version='1.0'?> should be written to the file by the .Save() method. UTF-8 is the default encoding in XML, so when encoding='utf-8' is not explicitly declared, it is assumed, there is no reason spend more time here. (As a hint for further research, explicitly specifying an encoding can be done by setting up an XmlWriter.)
As for the first question: You can use $xml.SelectNodes("/an/xpath/expression") to select the node range you want, and then write a foreach loop to import and append them one by one.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.