Querying Inner Text with Multiple Attributes

Please refer to the following node

<GKENTD.ACDSEGMENT accounted=“DR” index=“1”>2000</GKENTD.ACDSEGMENT>

I am stuck on how to query the inner text of ‘2000’ into a variable, by filtering on the two attributes. There are more of these I have to query (index=2,index=3,etc.)

This is what I have been playing with, I know its wrong, but can / should I even be using Select-XML in this case?

$GLSegment1 = Select-XML -XML $PY -XPATH “//EXTCHARGEDISTRIBUTION//GKENTD.ACDSEGMENT[accounted=‘DR’][index=‘1’]”

Any help would be most appreciated, this is the only thing I cannot get past for this one script I am doing…

Thank you

Your XPath query would look something like this:

Select-Xml -Xml $PY -XPath '//EXTCHARGEDISTRIBUTION/GKENTD.ACDSEGMENT[@accounted="DR" and @index="1"]'

When you’re filtering on attributes, you need the @ character before the name. All of the conditions go into a single set of brackets, and you can use “and”, “or” and parentheses the same way you’d use -and and -or in a PowerShell condition.

Thank you I was missing the @ , but I am still struggling with returning data. The script does not error, but no data is returned. I have a feeling something else is wrong and I am approaching this the wrong way. Would it be possible to post a script for a review?

I mean for me to post my script…

Sure, you can attach it here as a txt file.

I have attached my script and the example XML I am working with, I also attached the script that was successful in returning all the data elements in the entire document, I am trying to apply that same type of query to the loops in my script. Where I am struggling is how to return the values for only the current node. This is an AP Voucher with a Header and multiple lines, and I need to manipulate certain Inner Text for each line individually, while I am in the node loop. Again, I hope this is not asking too much, and I would be most grateful for any help that would get me past this roadblock

Here are the other files…

In “Success.txt”, you’ve got a slight bug:

$xx = ($xmlObject | Where-Object {$_.Accounted -eq "DR" -and $_.index="1"}).InnerXml

That second “=” sign ($_.index = “1”) should be another “-eq” operator; don’t mix those up. = is for variable assignments, -eq is for testing equality. You have similar code in pscript.txt which doesn’t have the same bug (both -eq operators are in the right places).

As for pscript.txt, I’m not certain what problem you’re having. I ran it on the sample XML you posted, and it successfully injected the ORIGREF and SUPPINVNUM elements into both PAYLINE elements. It also output some text to the console along the way. Here’s the output that I got:

PS C:\source\Temp> C:\Source\Temp\test.ps1

#text
-----
10080
10104-10
2000
2000
10080
10104-10
2000
2000
C:\source\Temp\test.xml

One thing that may be throwing you off are lines containing the #text header, and 10080 / 10104-10. Those are showing up because the calls to $node.AppendChild($e) return the value of $e (which would allow you to chain together fluent calls). In a PowerShell script, you’d want to suppress that output by using Out-Null, assigning to a variable / $null, or casting to [void] (whatever your preference):

$null = $node.AppendChild($e)

That just leaves the lines containing “2000”. In the sample XML file, each of the two PAYLINE elements has two child elements somewhere in the tree that match your filter, and all 4 of them have an InnerText value of 2000.

I did mess up the success one, but it does work, I just sent it to you incorrectly.

My problem is that I do not know how to get that query, that returns all the data in the success.txt, to return the data for JUST the current node while in the loop of that node. So basically all the ACDSEGMENT VALUES under the current node.

I do not get the same results as you when I run psscript, I only get

10080
10104-10

10080
10104-10

Which is the script showing the ORIGREF and REMITTANCE Values it is writing on each line

I do not see any “2000” that is what I am missing… I do not understand why you get that and I don’t. Is it a Powershell version issue?

The problem is not with populating the new nodes, that was always working - sorry for the confusion.

Ah, are you using PowerShell 2.0, by chance? PowerShell 3.0 added a feature called “Member Enumeration”, which would come into play on this line:

$DRSegment1 = ($node.SelectNodes("//DATASTREAM.ACDSEGMENT") | Where-Object {$_.accounted -eq "DR" -and $_.index -eq "1"}).InnerXML

If the expression in parentheses evaluates to an array, then the “.InnerXML” part of the expression tries to read an “InnerXML” property from System.Array (in PowerShell 2.0), which doesn’t exist. In PowerShell 3.0 or later, after seeing that there’s no InnerXML property and that the expression on the left of the . is a collection, it tries evaluating the InnerXML property for you on every element of the collection.

Here’s how you could make that line work in 2.0 (with some line breaks added for clarity):

$DRSegment1 = $node.SelectNodes("//DATASTREAM.ACDSEGMENT") |
              Where-Object {$_.accounted -eq "DR" -and $_.index -eq "1"} |
              Select-Object -ExpandProperty InnerXML

hi,

I have a stupid question. Why not use something like this (XML dot notation):

$PY.LOAD_PAYABLE_005.DATAAREA.LOAD_PAYABLE.PAYLINE $PY.LOAD_PAYABLE_005.DATAAREA.LOAD_PAYABLE.PAYHEADER.ORIGREF

Cheers

Tore

Thank you for the tip on Powershell 2.0 - yes I am using 2.0 and I made my adjustments and I am now getting data back, unfortunately, I am still unable to get JUST the data for the current node. Inside of my node loop I am still returning ALL the values for every PAYLINE, not just the value for the current PAYLINE.

Tore Groneog - the problem was never with the ability to read the Header values into the PAYLINE, the problem remains that I am unable to take a child Node that exists in the current node I am on in a loop, that has multiple attributes associated ,and return JUST the InnerText value for the node that exists in that current node not ALL the nodes.

Ah, I missed that. An xpath expression that starts with “/” searches from the root of the document (apparently even if you call it on a child node rather than the document’s root). Assuming you want to stick with the “//” notation that allows you to find an element by name without having to know the entire path, adding a “.” to the beginning of the XPath seems to work:

“.//DATASTREAM.ACDSEGMENT

Dave - YES! that is what I needed, it works fine now. Thank you very much for the quick and helpful responses…

OK - I have one more problem, then I am done with this script.

Consider the following XML BLock:

    &lt;TAX&gt;
      &lt;AMOUNT qualifier="TAX" type="T"&gt;
        &lt;VALUE&gt;10&lt;/VALUE&gt;
        &lt;NUMOFDEC&gt;2&lt;/NUMOFDEC&gt;
        &lt;SIGN&gt;+&lt;/SIGN&gt;
        &lt;CURRENCY /&gt;
        &lt;DRCR /&gt;
      &lt;/AMOUNT&gt;
      &lt;AMOUNT qualifier="TAXBASE" type="T"&gt;
        &lt;VALUE&gt;52&lt;/VALUE&gt;
        &lt;NUMOFDEC&gt;0&lt;/NUMOFDEC&gt;
        &lt;SIGN&gt;+&lt;/SIGN&gt;
        &lt;CURRENCY /&gt;
        &lt;DRCR /&gt;
      &lt;/AMOUNT&gt;

If I use the following:

$TAXAMT = Select-XML -Xml $PY -XPath “//PAYHEADER//TAX//AMOUNT[@qualifier=‘TAX’]”

Then $TAXAMT contains the following if I echo it:

<VALUE>10</VALUE><NUMOFDEC>2</NUMOFDEC><SIGN>+</SIGN><CURRENCY /><DRCR />

So I am struggling with the next step - how do I get the VALUE or the NUMOFDEC inner text values to be returned only?

The actual XML block should be :

<TAX>
<AMOUNT qualifier=”TAX” type=”T”>
<VALUE>10</VALUE>
<NUMOFDEC>2</NUMOFDEC>
<SIGN>+</SIGN>
<CURRENCY />
<DRCR />
</AMOUNT>
<AMOUNT qualifier=”TAXBASE” type=”T”>
<VALUE>52</VALUE>
<NUMOFDEC>0</NUMOFDEC>
<SIGN>+</SIGN>
<CURRENCY />
<DRCR />
</AMOUNT>
</TAX>

I left out the closing TAX node on the previous one. So there are two nodes under TAX, both are AMOUNT with different qualifiers. I need to get the qualifier =‘TAX’ node, and return the Inner Text from VALUE

I have rooted around for a few hours trying to understand how to filter ELEMENTS, etc. and nothing I am doing is working. I have tried several different things that I will not post here, I am just showing you what I have gotten to work somewhat which is:

$TAXAMT = Select-XML -Xml $PY -XPath “//PAYHEADER//TAX//AMOUNT[@qualifier=‘TAX’]”

Thanks

Anytime you use Select-XML, it returns an object of type Microsoft.PowerShell.Commands.SelectXmlInfo. To get to the actual returned value, you’d need to look at the Node property of the SelectXmlInfo object. In addition, your current XPath is set up to return a reference to the <AMOUNT> node. You can either keep that and add some additional code to retrieve the child data from <AMOUNT> that you’re interested in, or you can modify the original XPath expression to return it directly. (Which approach you take depends on whether you need to do anything else based on the parent node.)

For example:

$TAXAMT = (Select-Xml -Xml $PY -XPath '//PAYHEADER/TAX/AMOUNT[@qualifier="TAX"]/NUMOFDEC/text()').Node.Value

Note: If the XPath returns multiple nodes, this code will require at least PowerShell version 3.0 to execute properly. For PowerShell 2.0 compatibility, you’d do this:

$TAXAMT = Select-Xml -Xml $PY -XPath '//PAYHEADER/TAX/AMOUNT[@qualifier="TAX"]/NUMOFDEC/text()' |
          ForEach-Object { $_.Node.Value }

In either case, $TAXAMT will be an array of strings if there are multiple nodes that match your XPath criteria, a single string if only one node matches, and $null if there were no matches.

OK - Understand returning an array of values, and I understand, I think, about starting with a “.” to be on the current node. So If I have to execute a script similar to this:

$TAXAMT = Select-Xml -Xml $PY -XPath ‘//PAYHEADER/TAX/AMOUNT[@qualifier=“TAX”]/VALUE/text()’ |
ForEach-Object { $_.Node.Value }

This will be fine, because there will be only one PAYHEADER, so it will always return one value.

However, inside a current loop

$LINETOT = Select-Xml -Xml $PY -XPath ‘.//AMOUNT[@qualifier=“EXTENDED”]/VALUE/text()’ |
ForEach-Object { $_.Node.Value }

Why is that second one not working?

I also tried:

$LINETOT = $node.SelectNodes(".//AMOUNT/VALUE/text()") |
          Where-Object {$_.qualifier -eq "EXTENDED" -and $_.type -eq "T"} |
          ForEach-Object { $_.Node.Value }

And got nothing.

I am really sorry about this, I am just under a really tight deadline and I have never gone this deep into Powershell before…

Thanks

I am sorry, I know I said I was done, but I thought getting that answer above would solve this problem…

I’m not sure what you’re having problems with right now. When you say it’s “not working”, what do you mean? Are you getting no values, or too many values?

One thing I notice is that you’re calling Select-Xml and passing in $PY as the node, which means you’re querying the entire XML document. Based on the context if your last post, it sounds like you meant for that to be a search only under a particular child node (so you’d need to pass in that child node to Select-XML instead of $PY, or use $node.SelectNodes as in the previous posts.)

ok - I am not being specific, you are right… I am going to take another crack at it and get back you with more specific questions…