1

I am looping through multiple XML files in a directory. Each file has identical structure:

<input>
    <filePattern>
        <marketCode>cbf_d</marketCode>
        <format>txt</format>
    </filePattern>
</input>
<input>
    <filePattern>
        <marketCode>lvd_cbf_b</marketCode>
        <format>csv</format>
    </filePattern>
</input>
<input>
    <filePattern>
        <marketCode>cbf_a</marketCode>
        <format>zip</format>
    </filePattern>
</input>

My purpose is to loop through each input node within a file, extract InnerText values from marketCode and format and then concatenate them into a single string:

Get-ChildItem -Path 'C:\test\cbf\*merge*' -Recurse |
ForEach-Object {
    $xml_file = $_ 
    
    $content = [xml](Get-Content $xml_file)
    $nodes = $content.SelectNodes('//input')    

    
    foreach ($node in $nodes) {
        $marketCode = $node.SelectNodes('//input/filePattern/marketCode').InnerText
        $format = $node.SelectNodes('//input/filePattern/format').InnerText
        
                                    
    }
    #Trying to cancatenate InnerText values here:
    $marketCode + '_' + $format
    
        
}

The output that I get:

cbf_d
lvd_cbf_b
cbf_a
_
txt
csv
zip

The output that I expect:

cbf_d_txt
lvd_cbf_b_csv
cbf_a_zip

How to properly concatenate values of InnerText?

2 Answers 2

2

By using //input you are resetting your base path to "global" - which means ignoring your context node $node. The solution is to use a relative path (relative to $node) in your SelectNodes expressions. Furthermore, you only concatenated the strings once per file - and not once per <input> element - so the concatenation operation needs to be shifted into the loop.

Get-ChildItem -Path 'C:\test\cbf\*merge*' -Recurse |
ForEach-Object {
    $xml_file = $_ 
    
    $content = [xml](Get-Content $xml_file)
    $nodes = $content.SelectNodes('//input')    

    
    foreach ($node in $nodes) {
        $marketCode = $node.SelectNodes('filePattern/marketCode').InnerText
        $format = $node.SelectNodes('filePattern/format').InnerText
        #Trying to concatenate InnerText values here:
        $marketCode + '_' + $format
    }
}

The output is as desired.

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

Comments

1

To complement zx485's helpful answer, which explains the problems with your attempt well and offers an effective solution, with a more concise solution that uses the Select-Xml cmdlet:

Get-ChildItem C:\test\cbf\*merge* -Recurse |
  Select-Xml '//input/filePattern' | 
    ForEach-Object { $_.Node.marketCode, $_.Node.format -join '_' }
  • Select-Xml can perform XPath queries directly on input files, without having to parse the file content into an XML DOM ([xml] (System.Xml.XmlDocument)) manually.

  • It outputs SelectXmlInfo wrapper objects with metadata, whose .Node property contains the matching node(s).

    • As an aside: Given that the metadata is often not needed, it would be convenient if Select-Xml could be instructed to return the nodes only, which is the subject of GitHub feature request #13669.
  • Thanks to PowerShell's convenient adaption of the XML DOM, you can access child elements on the nodes returned simply as properties (e.g., .marketCode to access the <marketCode> child element), and if those elements only have text content (only a child text node), the text is directly returned (no need for .InnerText).

Comments

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.