1

I am working with an XML document and I am not sure how to parse it to find a particular node.

In the example below I'd be searching for the ProductB and expecting to identify the Bee Hive

<PRODUCTS>
    <PRODUCT_LEVEL_1>
        <PRODUCT_ID>ProductA</PRODUCT_ID>
        <PRODUCT_NAME>Ant Hill</PRODUCT_NAME>
        <PRODUCT_ID>ProductB</PRODUCT_ID>
        <PRODUCT_NAME>Bee Hive</PRODUCT_NAME>
        <PRODUCT_ID>ProductC</PRODUCT_ID>
        <PRODUCT_NAME>Centipede Hotel</PRODUCT_NAME>
    </PRODUCT_LEVEL_1>
</PRODUCTS>

I'm not even sure how to refer to this format of XML which is making it difficult to search for.

Any assistance gratefully received.

Edit: At present I am loading the XML from a file using the following:

System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
$file = resolve-path($inputFile)
$xd.load($file)
$nodelist = $xd.selectnodes("/PRODUCTS/PRODUCT_LEVEL_1")
foreach ($documentNode in $nodelist) {
    #do some stuff
}

The actual XML file has loads of other junk in too but I stripped it out in the hopes of focusing on the bits I need. Perhaps I have stripped too much and the context has been lost?

6
  • EDIT: Why does the comment section not keep my line breaks?! Thanks for the quick response PetSerAl. I am wondering how to use that in my existing code. At the moment I have loaded the XML document and selected all the nodes of <PRODUCT> into a $nodelist. I am then using a foreach to loop through each <PRODUCT> node. Commented Dec 16, 2015 at 18:21
  • I can't figure out how to post the code - every time I hit enter it takes it as a comment!! Commented Dec 16, 2015 at 18:26
  • Thanks for that too! :) Commented Dec 16, 2015 at 18:28
  • Replace your SelectNodes() argument with the -Xpath value @PetSerAl suggested Commented Dec 16, 2015 at 18:33
  • Ah - I see. Much appreciated to you both. Commented Dec 16, 2015 at 18:34

1 Answer 1

1

You can use XPath for this task. XPath have axes, which allows you to refer preceding and following elements of document:

filter Prepare-StringForXPath {
    param(
        [Parameter(ValueFromPipeline)]
        [String]$String
    )
    if($String -notlike '*''*') {
        "'$String'"
    } elseif($String -notlike '*"*') {
        """$String"""
    } else {
        “concat($(($String -split '(?<=''[^"]*)(?=")|(?<="[^'']*)(?='')' | Prepare-StringForXPath) -join ', '))”
    }
}

$xd = [Xml]@'
<PRODUCTS>
    <PRODUCT_LEVEL_1>
        <PRODUCT_ID>ProductA</PRODUCT_ID>
        <PRODUCT_NAME>Ant Hill</PRODUCT_NAME>
        <PRODUCT_ID>ProductB</PRODUCT_ID>
        <PRODUCT_NAME>Bee Hive</PRODUCT_NAME>
        <PRODUCT_ID>ProductC</PRODUCT_ID>
        <PRODUCT_NAME>Centipede Hotel</PRODUCT_NAME>
    </PRODUCT_LEVEL_1>
</PRODUCTS>
'@

$ProdName = Read-Host 'Input product name'
$ProdID = $xd.SelectSingleNode("//PRODUCT_NAME[.=$(Prepare-StringForXPath $ProdName)]/preceding-sibling::PRODUCT_ID[1]").InnerText
"Product ID for '$ProdName' is '$ProdID'."

$ProdID = Read-Host 'Input product ID'
$ProdName = $xd.SelectSingleNode("//PRODUCT_ID[.=$(Prepare-StringForXPath $ProdID)]/following-sibling::PRODUCT_NAME[1]").InnerText
"Product name for '$ProdID' is '$ProdName'."
Sign up to request clarification or add additional context in comments.

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.