Querying Inner Text with Multiple Attributes

This topic contains 20 replies, has 3 voices, and was last updated by Profile photo of Devin McMahon Devin McMahon 3 years, 3 months ago.

  • Author
    Posts
  • #13706
    Profile photo of Devin McMahon
    Devin McMahon
    Participant

    Please refer to the following node

    2000

    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

  • #13707
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    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.

  • #13724
    Profile photo of Devin McMahon
    Devin McMahon
    Participant

    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?

  • #13725
    Profile photo of Devin McMahon
    Devin McMahon
    Participant

    I mean for me to post my script...

  • #13726
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

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

  • #13782
    Profile photo of Devin McMahon
    Devin McMahon
    Participant

    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

  • #13784
    Profile photo of Devin McMahon
    Devin McMahon
    Participant

    Here are the other files..

  • #13787
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    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.

  • #13794
    Profile photo of Devin McMahon
    Devin McMahon
    Participant

    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.

  • #13814
    Profile photo of Tore Groneng
    Tore Groneng
    Participant

    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

  • #13844
    Profile photo of Devin McMahon
    Devin McMahon
    Participant

    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.

  • #13845
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    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"

  • #13846
    Profile photo of Devin McMahon
    Devin McMahon
    Participant

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

  • #13876
    Profile photo of Devin McMahon
    Devin McMahon
    Participant

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

    Consider the following XML BLock:



    10
    2
    +




    52
    0
    +



    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:

    102+

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

  • #13877
    Profile photo of Devin McMahon
    Devin McMahon
    Participant

    The actual XML block should be :



    10
    2
    +




    52
    0
    +



    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

  • #13878
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    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 node. You can either keep that and add some additional code to retrieve the child data from 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.

  • #13901
    Profile photo of Devin McMahon
    Devin McMahon
    Participant

    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...

  • #13902
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    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.)

  • #13903
    Profile photo of Devin McMahon
    Devin McMahon
    Participant

    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..

  • #13905
    Profile photo of Devin McMahon
    Devin McMahon
    Participant

    Dave – I got it 🙂

    $LINETOT = Select-Xml -Xml $node -XPath './/AMOUNT[@qualifier="EXTENDED"]/VALUE/text()' |
    ForEach-Object { $_.Node.Value }

    Thank you for your patience...

  • #13801
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    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
    
    

You must be logged in to reply to this topic.