1

Is it possible to define two "SelectSingleNode" statements while narrowing down a search for a specific XML value?

My XML structure is as follows:

<beans>
  <bean id="MenuGet">
    <property name="name">
    <property name="MapOverride">
      <map>
          <entry key="node.infoblock" value="k4jk2jb54B$T45bt2j5ktb3B%$">
      </map>
    </property>
  </bean>
</beans>

<beans>
  <bean id="SystemGet">
    <property name="name">
    <property name="MapOverride">
      <map>
          <entry key="node.infoblock" value="b34t34bhj54b%B#Y$%Bn45ht5h">
      </map>
    </property>
  </bean>
</beans>

The statement I'm using to call the value I'm trying to pull from the first "beans" tag is below but I'm not sure if it's right or would work:

$statement=$XMLFile.beans.bean.SelectSingleNode("id[contains(@key="Menu")]").property[1].map.SelectSingleNode("entry[@key='node.infoblock']").value

murkywaters
  • 95
  • 2
  • 11

2 Answers2

1

There are two entries with key "node.infoblock" in that file. If you want one entry per bean then you shouldn't use SelectSingleNode. It makes more sense to get them by using SelectNodes.

Second, the Xml file you posted cannot be parsed because it has format issues (there is no root node and many unclosed tags). It should look like this:

<beans>
  <bean id="MenuGet">
    <property name="name" />
    <property name="MapOverride">
      <map>
          <entry key="node.infoblock" value="k4jk2jb54B$T45bt2j5ktb3B%$" />
      </map>
    </property>
  </bean>
  <bean id="SystemGet">
    <property name="name" />
    <property name="MapOverride">
      <map>
          <entry key="node.infoblock" value="b34t34bhj54b%B#Y$%Bn45ht5h" />
      </map>
    </property>
  </bean>
</beans>

If you save that file, entries can be retrieved running this code:

$xmlFilePath = "c:\temp\file.xml"
$doc = new-object System.Xml.XmlDocument
$doc.load($xmlFilePath)
$entries = $doc.SelectNodes("//entry[@key='node.infoblock'][1]")
foreach($entry in $entries)
{
    Write-Host $entry.value
}

It prints:

k4jk2jb54B$T45bt2j5ktb3B%$

b34t34bhj54b%B#Y$%Bn45ht5h

derloopkat
  • 6,232
  • 16
  • 38
  • 45
1

A single XPath query and therefore a single .SelectSingleNode() call can do what you want:

$query = '/*/beans/bean[contains(@id,"Menu")]/property/map/entry[@key="node.infoblock"]'
$XMLFile.SelectSingleNode($query).value

The above yields k4jk2jb54B$T45bt2j5ktb3B%$

Note:
- To match multiple nodes, simply use .SelectNodes() in lieu of .SelectSingleNode(); see the next section for an alternative using the Select-Xml cmdlet.
- The command assumes that your XML input is well-formed, which it currently isn't (missing root element, some missing closing tags); because the root element name isn't known, the query starts with /*/.


Alternative: using the Select-Xml cmdlet

Select-Xml essentially wraps the .NET .SelectNodes() method and adds the ability to process multiple inputs as well as the ability to target XML files by path directly:

$query = '/*/beans/bean[contains(@id,"Menu")]/property/map/entry[@key="node.infoblock"]'
(Select-Xml -LiteralPath file.xml -XPath $query).Node.value

Note the need to access the intermediate .Node property, because Select-Xml wraps the results in [Microsoft.PowerShell.Commands.SelectXmlInfo] instances.


As for what you tried:

If you're calling .SelectSingleNode() (i.e., if you're using XPath) anyway, it is better to locate the target node by XPath query only - by calling .SelectSingleNode() directly on your document object, $XMLFile - and to limit use of PowerShell's dot notation to the result of that call - by accessing the .value attribute as a property.

See this answer of mine for a comparison of dot notation and Select-Xml use.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Are there any good resources out there to learn how Powershell interfaces with XML or JSON files? Seems like there aren't that many that cover it. – murkywaters Apr 16 '18 at 18:20
  • @murkywaters: Re XML: please see my update, which hopefully gets you started; re JSON: JSON support is pretty straightforwards in PowerShell; so reading the `ConvertFrom-Json` and `ConvertTo-Json` help topics may be all you need. – mklement0 Apr 16 '18 at 18:48