Conditional on empty SelectNodes set - powershell

Given XML like this
<?xml version="1.0"?>
<Definitions>
<Products>
<Product_Group id="Revit">
<Product id="RVT2017">
<RSAccelerator>RSACCELERATOR2017</RSAccelerator>
<ShortcutPath os="6.0 6.1">C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Autodesk\Revit 2017</ShortcutPath>
<ShortcutPath os="6.2 6.3 10.0">C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Revit 2017</ShortcutPath>
</Product>
</Product_Group>
</Products>
</Definitions>
I want to test for the presence of the OS attribute so I can handle getting the value differently when there are two nodes differentiated by that attribute value vs no attribute at all.
I would have thought this would work, with appropriate values for the two variables.
if ($script:pxResources.SelectNodes("//Product[#id='$product']/$resource[#os]")) {
However, this is returning true even when no nodes are selected. I can use
if ($script:pxResources.SelectNodes("//Product[#id='$product']/$resource[#os]").count -gt 0) {
but that seems clumsy. Is there a better way to handle this, or is testing for an empty set the only option?

AFAIK you will always have to test as SelectNodes will return a System.Xml.XPathNodeList object, which PowerShell will consider to be true even if it is empty.
Agreed adding some code to test is not pretty but AFAIK it's necessary.
My preferred method is IsNullOrEmpty:
[String]::IsNullOrEmpty(<thing>)
# example
$exp = $script:pxResources.SelectNodes("//Product[#id='$product']/$resource[#os]")
if (-not [String]::IsNullOrEmpty($exp)) {# do something}

There's a;so the approach where you don't use XPath:
[xml]$xml = #"
<?xml version="1.0"?>
<Definitions>
<Products>
<Product_Group id="Revit">
<Product id="RVT2017">
<RSAccelerator>RSACCELERATOR2017</RSAccelerator>
<ShortcutPath os="6.0 6.1">C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Autodesk\Revit 2017</ShortcutPath>
<ShortcutPath os="6.2 6.3 10.0">C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Revit 2017</ShortcutPath>
<ShortcutPath os="">C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Anything\Revit 2017</ShortcutPath>
<ShortcutPath>C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Something\Revit 2017</ShortcutPath>
</Product>
</Product_Group>
</Products>
</Definitions>
"#
$product = "RVT2017"
$resource = "ShortcutPath"
($xml.Definitions.Products.Product_Group.Product | Where-Object { $_.id -eq $product }).$resource | ForEach-Object {
if ($null -eq $_.os) {
Write-Host "'os' attribute missing on $_"
}
elseif ([string]::IsNullOrWhiteSpace($_.os)) {
Write-Host "'os' attribute empty on element $($_.outerXml)"
}
else {
Write-Host "'os' = $($_.os) on element $($_.outerXml)"
}
}
This will output
'os' = 6.0 6.1 on element <ShortcutPath os="6.0 6.1">C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Autodesk\Revit 2017</ShortcutPath>
'os' = 6.2 6.3 10.0 on element <ShortcutPath os="6.2 6.3 10.0">C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Revit 2017</ShortcutPath>
'os' attribute empty on element <ShortcutPath os="">C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Anything\Revit 2017</ShortcutPath>
'os' attribute missing on C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Something\Revit 2017

Related

How in Powershell see all XML Levels

I have a test xml and I want to get the value from this line ATTRIBUTE NAME="News- offers_OPT_EMAIL">F
so I can check for the value F or T
if I do below I can get the title but how do I get the above line value.
[xml]$xml = Get-Content testFile.xml
$xml
$xml.CUSTOMERS.CUSTOMER.NAME.TITLE
sample XML code
<?xml version="1.0" encoding="UTF-8"?>
<CUSTOMERS xml:lang="en">
<CUSTOMER CREATED_DATE="2018-01-01 05:18:53.0" GROUP_ID="" ID="95656565">
<NAME>
<TITLE>M</TITLE>
<FIRST_NAME>Joe</FIRST_NAME>
<LAST_NAME>Smith</LAST_NAME>
</NAME>
<GENDER/>
<DATE_OF_BIRTH/>
<ADDRESS>
<ADDRESS_LINE_1>1 White House</ADDRESS_LINE_1>
<ADDRESS_LINE_2>Old Ave</ADDRESS_LINE_2>
<ADDRESS_LINE_3/>
<TOWNCITY>LONDON</TOWNCITY>
<COUNTY/>
<POSTCODE>18659</POSTCODE>
<COUNTRY>France</COUNTRY>
</ADDRESS>
<ADDRESS>
<ADDRESS_LINE_1>175 avenue de la division Leclerc</ADDRESS_LINE_1>
<ADDRESS_LINE_2/>
<ADDRESS_LINE_3/>
<TOWNCITY>Antony</TOWNCITY>
<COUNTY/>
<POSTCODE>92160</POSTCODE>
<COUNTRY>France</COUNTRY>
</ADDRESS>
<CONTACT_DETAILS>
<TELEPHONE MARKETING_OPT_IN="F" TYPE="MOBILE">0123456789</TELEPHONE>
<EMAIL MARKETING_OPT_IN="F">johnsmith#gmail.com</EMAIL>
</CONTACT_DETAILS>
<ATTRIBUTE NAME="News- offers_OPT_EMAIL">F</ATTRIBUTE>
<NOTE>NA</NOTE>
</CUSTOMER>
You could use SelectSingleNode or SelectNodes with an XPath expression. There are several options to achieve what you want, depending on your intention, but this would be one way to do it:
# finde the nodes
$nodes = $xml.SelectNodes("//*[local-name()='ATTRIBUTE'][#NAME='News- offers_OPT_EMAIL']")
# get value
$nodes.InnerText
Or if the value of the attribute doesn't matter, simply do:
$xml.customers.customer.attribute.'#text'

Updating Text In XML File [duplicate]

This question already has answers here:
Change XML Element value with PowerShell
(2 answers)
Closed 2 years ago.
I have the following xml file.
<Objects>
<Object>
<Property Name="Browser">Firefox</Property>
<Property Name="PDF">Adobe Reader</Property>
</Object>
I want to be able to update the word firefox using powershell script.
This is the powershell script that am working and is not working.
$xmlDoc = [XML](Get-Content "c:\Windows\personalsettings\PersonalSettings.xml")
foreach ($item in $xmlDoc.Objects.Object.Property)
{
$item.Name = 'Chrome'
}
$xmlDoc.Save("c:\Windows\personalsettings\PersonalSettings.xml")
[xml]$XML = #"
<Objects>
<Object>
<Property Name="Browser">Firefox</Property>
<Property Name="PDF">Adobe Reader</Property>
</Object>
</Objects>
"#
$XML.SelectSingleNode('//Property[#Name="Browser"]')
$XML.SelectSingleNode('//Property[#Name="Browser"]').InnerText = "TEST"
$XML.SelectSingleNode('//Property[#Name="Browser"]')
Results will be
> Name #text
> ---- -----
> Browser Firefox
> Browser TEST
What you are looking for is XPATH
Works like this
//Path Or NodeName[#AttributeName="Value Of Attribute"]
Once you get the Node you can then edit that node how you see fit.

INVALID_REQUEST: Field [order.avsDetails.billToFirstname] was not in charset [ISO-8859-1]

For some Reasons when I use OnTap MasterCard Extension, Any Arabic characters in shippment addresses throws an error:
INVALID_REQUEST: Field [order.avsDetails.billToFirstname] was not in charset [ISO-8859-1]
The extension link :
https://marketplace.magento.com/ontap-module-mastercard.html
Please help.
You can try encoding the data generated in the Builders (inside the Gateway/Request folder) by using plugins.
You can read more how to create plugins here that perform the encoding on all the fields in the builders when needed.
You will create a new module that is doing the modifications needed on the extension you took from the market.
To define your builder in this case your di.xml will look something like:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="\OnTap\MasterCard\Gateway\Request\ShippingDataBuilder">
<plugin name="jsparo_ontap_mastercard_gateway_request_shippingdatabuilder" type="Jsparo\MasterCard\Plugin\Gateway\Request\ShippingDataBuilder" sortOrder="1"/>
</type>
</config>
And the Plugin/Gateway/Request/ShippingDataBuilder.php that you will be something like:
<?php
namespace Jsparo\MasterCard\Plugin\Gateway\Request;
class ShippingDataBuilder {
public function afterBuild(array $subject, $result) {
array_walk_recursive($result, function(&$value) {
$value = mb_convert_encoding($value, 'ISO-8859-1', 'UTF-8');
}
return $result;
}
}
You will have to do this for all the builders that generate incorrect data.

web.config changes for KB3159706

i spent a few hours trying to code this by myself, but i don't know much about editing the web.config and all the examples i found don't come close to what i need. CHANGE#1 is unique because it does include the typical key=value.
I want to be able to script (PowerShell) the required modifications of Web.Config, only if the values do not already exist.
CHANGE#1:
Onsert this
(if not already there and "true"): multipleSiteBindingsEnabled="true"
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
CHANGE#2:
Insert this if not already there:
<endpoint address=""
binding="basicHttpBinding"
bindingConfiguration="SSL"
contract="Microsoft.UpdateServices.Internal.IClientWebService" />
<endpoint address="secured"
binding="basicHttpBinding"
bindingConfiguration="SSL"
contract="Microsoft.UpdateServices.Internal.IClientWebService" />
It goes between here:
<services>
<service
name="Microsoft.UpdateServices.Internal.Client"
behaviorConfiguration="ClientWebServiceBehaviour">
<!-- ... CODE FROM CHANGE#2 GOES HERE ... -->
</service>
</services>
This is the code so far for change#1 (not working):
$sWSUSwebConfig = "C:\Program Files\Update Services\WebServices\ClientWebService\Web.Config"
$xFileContent = [Xml](Get-Content $sWSUSwebConfig)
$root = $xFileContent.get_DocumentElement()
foreach ($item in $root."system.serviceModel"."serviceHostingEnvironment") {
if ($item."multipleSiteBindingsEnabled" -ine "true") {
$activeConnection = $root.serviceHostingEnvironment
$activeConnection.SetAttribute("multipleSiteBindingsEnabled", "true")
#$item.add."multipleSiteBindingsEnabled" = "true"
$iKeyFound = $true
}
}
$xFileContent.Save("c:\temp\web.config")
Reference for modifications: step 3 from kb3159706.

PowerShell - API data into XML

I am new to powershell and trying to write my first script.I am using PowerShell v2.0. I have the following script that makes an API call and gets data into $data variable.
$FullURL = $url1+$url2+$Url3
$client = New-Object System.Net.WebClient
$data = $client.DownloadString($FullURL)
Set-Content -Value $data -Path 'c:\API.txt'
$data outputs the below (example). Note - gettype() results are string. -
<attribute name="Business Unit">Platform</attribute>
<attribute name="Department">Channels Technology</attribute>
<attribute name="Team">Stackexchange</attribute>
<attribute name="Environment">World</attribute>
<attribute name="ServerModel">Synology</attribute>
<attribute name="datacentre">New York</attribute>
<attribute name="Application">PowerShell Teacher</attribute>
<attribute name="Description">Learn How To Use PowerShell</attribute>
I need to get the above into the below sample of the XML file, in between the attributes tags -
<selfAnnounce>
<enabled>true</enabled>
<retryInterval>60</retryInterval>
<requireReverseConnection>false</requireReverseConnection>
<probeName>
<hostname/>
<data>_</data>
<port/>
<data>-SA</data>
</probeName>
<managedEntity>
<name></name>
<attributes>
</attributes>
I am not sure where to begin with this one. I thought it would be something like the below but the results are all in one tag, maybe because they are not pscustom objects -
[xml]$XML = Get-Content $SelfannounceXMLEdit
$data | ForEach-Object {
$tempchild = $XML.CreateElement("Attributename")
$tempchild.set_InnerText($_)
$newType = $XML.netprobe.selfAnnounce.managedEntity.attributes.AppendChild($tempchild)
}
$XML.Save($SelfannounceXMLEdit)
This gives the following results which is obviously wrong for an XML file-
<attributes>
<Attributename><attribute name="Business Unit">Platform</attribute> <attribute name="Department">Channels Technology</attribute> <attribute name="Team">Stackexchange</attribute> <attribute name="Environment">World</attribute> <attribute name="ServerModel">Synology</attribute> <attribute name="datacentre">New York</attribute> <attribute name="Application">PowerShell Teacher</attribute> <attribute name="Description">Learn How To User PowerShell</attribute></Attributename>
</attributes>
Results should look like the following -
<?xml version="1.0" encoding="ISO-8859-1"?>
<netprobe compatibility="1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://google.com/netprobe.xsd">
<selfAnnounce>
<enabled>true</enabled>
<retryInterval>10</retryInterval>
<requireReverseConnection>false</requireReverseConnection>
<probeName>
<hostname />
<data>_</data>
<port />
<data>-SA</data>
</probeName>
<managedEntity>
<name></name>
<attributes>
<attribute name="Business Unit">Platform</attribute>
<attribute name="Department">Channels Technology</attribute>
<attribute name="Team">Stackexchange</attribute>
</attributes>
<types>
<type>Core</type>
<type>Core Windows</type>
<!--Autogenerated types-->
<!--End of Autogenerated types-->
</types>
</managedEntity>
<gateways>
<gateway>
<hostname>MFT556</hostname>
<port>1234</port>
</gateway>
</gateways>
</selfAnnounce>
</netprobe>
Please help me resolve this issue. I have tried converting $data into xml but keep getting errors. I have tried exporting the API as XML but get errors. any help is appreciated.
You can't modify an element type AFAIK, so the AttributeName-node is useless.
...managedEntity.attributes is empty, which means dot-accesing it will return an empty string which doesn't have a AppendChild()
I would create an xml-document for each string from the API and import the attribute node to the "real" xml-file and append it. Remember to use ex SelectSingleNode() to actually get the attributes-node, especially the first time when it's empty. Try:
[xml]$XML = Get-Content $SelfannounceXMLEdit
($data -split "`n") | Where-Object { $_.Trim() } | ForEach-Object {
#Create XMLdocument for <attribute name="foo">bar</attribute>
$tempchild = [xml]$_.Trim()
#Import the attribute-node in the temp xmldocument to the "real" document context
$attribute = $xml.ImportNode($tempchild.attribute, $true)
#Append attribute-node
$newType = $XML.netprobe.selfAnnounce.managedEntity.selectsinglenode("attributes").AppendChild($attribute)
}
$XML.Save($SelfannounceXMLEdit)