1

I have a text file with this content:

123asdf
abdfeef
22343sf
dfafdsf

The content of this file is changing.

I want to create an xml file, where one element contains the text file content.

I tried this, but all the content is in the same element:

$Getfile = Get-Content .\DeleteFile.txt
$TimeStamp = [System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId(
        (Get-Date), 'Taipei Standard Time')
$delete_file = @"
<?xml version="1.0" encoding="UTF-8"?>
    <Handling>
        <Deleted>
            <1>$Getfile</1>
        </Deleted>
        <TimeStamp>$TimeStamp</TimeStamp>
    </Handling>
"@
$delete_file | Out-File .\DeleteFile.xml

Output:

<?xml version="1.0" encoding="UTF-8"?>
<Handling>
    <Deleted>
        <1>123asdf abdfeef 22343sf dfafdsf</1>
    </Deleted>
    <TimeStamp>12/04/2019 17:49:06</TimeStamp>
</Handling>

My expectation was that the output would be like this:

<?xml version="1.0" encoding="UTF-8"?>
<Handling>
    <Deleted>
        <1>123asdf</1>
        <2>abdfeef</2>
        <3>22343sf</3>
        <4>dfafdsf</4>
    </Deleted>
    <TimeStamp>12/04/2019 17:49:06</TimeStamp>
</Handling>

Can anyone help me please?

2 Answers 2

3

The contents go into same element, as you are not creating new XML elements. To add elements, create a XML object and use its CreateElement and AppendChild methods. Like so,

# Skeleton document that is filled later
[xml]$d = @'
<Handling>
<Deleted />
<TimeStamp />
</Handling>
'@

# Dummy data, read these from a file instead    
$data = @('123asdf', 'abdfeef', '22343sf', 'dfafdsf')

# XPath selects the node under which new elements are added
$n = $d.SelectSingleNode('/Handling/Deleted')

# Loop through input data    
for($i=0; $i -lt $data.length; ++$i) {
    # Create new element, Element name is from loop counter
    $e = $d.CreateElement(($i+1).ToString())
    $e.InnerText = $data[$i]
    # Add the new node into document
    $n.AppendChild($e) | out-null
}
# Display the results.
$d.save([console]::out)

Output:

<?xml version="1.0" encoding="ibm850"?>
<Handling>
  <Deleted>
    <1>123asdf</1>
    <2>abdfeef</2>
    <3>22343sf</3>
    <4>dfafdsf</4>
  </Deleted>
  <TimeStamp />
</Handling>
Sign up to request clarification or add additional context in comments.

4 Comments

Hi @vonPryz Thank you. But how do I output the result to file? I try$d.save(".\DeleteFile.xml") but I does not work
@SBR: Try $d.save("$PWD\DeleteFile.xml") - .NET uses a different working dir. than PowerShell.
I add this $File = New-Item -ItemType File .\File.xml -Force $d.save($File) It works!
+1 for a robust solution in principle, but note that the resulting XML file will be invalid, because XML element names mustn't start with a digit. (I'm baffled that .CreateElement() doesn't complain).
2

vonPryz' helpful answer leverages the .NET XmlDocument API to construct the document.

A solution using an expandable string (string interpolation), as you've attempted, is possible, namely by including statements that enumerate the $Getfile array and construct numbered XML elements from its elements, via $(...), the sub-expression operator.

Two caveats:

  • XML element names must not start with a digit, so trying to create elements named 1, 2, ... will result in an invalid XML document. Therefore, the code below uses e1, e2, ... instead.

  • The code uses Set-Content -Encoding Utf8 to save the output file, so as to ensure that the character encoding used - UTF-8 - matches the encoding specified in the XML declaration (encoding="UTF-8").

    • At least in Windows PowerShell Out-File / > would result in UTF-16LE encoding instead.
      PowerShell [Core] v6+, however, consistently defaults to BOM-less UTF-8.
    • In Windows PowerShell, Set-Content -Encoding Utf8 creates a UTF-8 file with a BOM, unlike in PowerShell [Core] v6+. In Windows PowerShell, you cannot opt out of the BOM; in PowerShell [Core] v6+, you can opt in with -Encoding utf8BOM.
$Getfile = "123asdf", "abdfeef", "22343sf", "dfafdsf" # sample input

$delete_file = @"
<?xml version="1.0" encoding="UTF-8"?>
    <Handling>
        <Deleted>
$(
  $i = 0
  $(foreach ($el in $GetFile) {
    ++$i
"            <e$i>$el</e$i>"
  }) -join [Environment]::NewLine
)
        </Deleted>
        <TimeStamp>$TimeStamp</TimeStamp>
    </Handling>
"@

$delete_file | Set-Content -Encoding Utf8 .\DeleteFile.xml

1 Comment

Glad to hear it was helpful, @SBR; my pleasure.

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.