I want to parse out the key fields and Data table information from here with PowerShell.
I only want the datatable name if there is a keyfield so in the example below I do not want CC:Attribute.
I also want to output things to a text file.
I want to have a text file that is created that holds the Data table name & Access as well as all the key fields and what they are.
This is the code I have so far:
[xml]$global:xmldata = get-content "C:\hackathon\Mfg.xml"
$xmldata2 = $xmldata.SchemaPackage.Tables
$SField = $xmldata2.DataTable.KeyFields | %{$_.StringField}
$Reffield = $xmldata2.DataTable.KeyFields | %{$_.ReferenceField}
$table = $xmldata2 | %{$_.DataTable}
Xml File:
<?xml version="1.0" encoding="utf-8"?>
<SchemaPackage Namespace="Mfg" xmlns="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DataTable Name="CC::Attribute">
<DataFields>
</DataFields>
</DataTable>
<DataTable Name="PlannerCode" Access="WW">
<Licenses>Manufacturing, DemandManagement</Licenses>
<Flags>
</Flags>
<KeyFields>
<StringField Name="Value"/>
<ReferenceField Name="Site" Target="Core::Site" SetField="PlannerCodes"/>
</KeyFields>
<DataFields>
<StringField Name="Description"/>
</DataFields>
</DataTable>
</SchemaPackage>
Despite the edit you made, I can only get your XML to validate if I modify it slightly (cleaning the opening XML tag and removing the SchemaPackage namespace). Regardless,
if you're experiencing no issues with your XML import then it's fine.
Here I'm just constructing the XML object from a herestring because I haven't got it in a file on disk.
[xml]$xmldata = #"
<xml>
<DataTable Name="CC::Attribute">
<DataFields>
</DataFields>
</DataTable>
<DataTable Name="PlannerCode">
<Licenses>Manufacturing, DemandManagement</Licenses>
<Flags>
</Flags>
<KeyFields>
<StringField Name="Value"/>
<ReferenceField Name="Site" Target="Core::Site" SetField="PlannerCodes"/>
</KeyFields>
<DataFields>
<StringField Name="Description"/>
</DataFields>
</DataTable>
</xml>
"#
# Filter DataTable nodes for those with a KeyFields child node.
$DataTablesWithKeyFields = $xmldata.xml.DataTable | Where-Object { $_.KeyFields }
$DataTableName = $DataTablesWithKeyFields.Name
$StringFieldData = $DataTablesWithKeyFields.KeyFields.ReferenceField
$ReferenceFieldData = $DataTablesWithKeyFields.KeyFields.ReferenceField
I'm not sure if that's what you're after. $DataTablesWithKeyFields could be an array depending on your XML file so you may need to loop it to extract the information you require.
Since we're working with XML, one of the querying options is XPath!
You can select only DataTable nodes that have a KeyFields child with the following XPath expression:
/SchemaPackage/DataTable[KeyFields]
You can use Select-Xml:
Select-Xml -Path C:\hackathon\Mfg.xml -XPath /SchemaPackage/DataTable[KeyFields] |Select-Object -Expand Node
or pass the expression as an argument to the SelectSingleNodes() method:
[xml]$xmldata = Get-Content C:\hackathon\Mfg.xml
$xmldata.SelectNodes('/SchemaPackage/DataTable[KeyFields]')
Related
Just getting started with XQuery using BaseX.
The XML structure that I did not create and have no control over, looks like this:
2002test.xml:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:runSearchResponse xmlns:ns2="http://externalapi.business.footprints.numarasoftware.com/">
<return>
<_items>
<_containerDefinitionId>1234</_containerDefinitionId>
<_containerDefinitionName>Service Desk</_containerDefinitionName>
<_itemDefinitionId>2244</_itemDefinitionId>
<_itemDefinitionName>Service Request</_itemDefinitionName>
<_itemId>9989</_itemId>
<_itemFields>
<itemFields>
<fieldName>Icon Name</fieldName>
<fieldValue>
<value>default_ticket.png</value>
</fieldValue>
</itemFields>
<itemFields>
<fieldName>emailCustomerUpdate</fieldName>
<fieldValue>
<value>false</value>
</fieldValue>
</itemFields>
<itemFields>
<fieldName>bestContactNumber_Customer</fieldName>
<fieldValue>
<value/>
</fieldValue>
</itemFields>
</_itemFields>
</_items>
</return>
</ns2:runSearchResponse>
</soap:Body>
</soap:Envelope>
Doing a simple:
for $x in doc("2002test.xml")
return $x
returns my entire document as expected.
Any attempt to drill into it is not working for me however.
I've tried:
for $x in doc("2002test.xml")/soap:Envelope/soap:Body/ns2:runSearchResponse/return
return $x/_items/itemFields/fieldName
But the long path throws a "No namespace declared for 'soap:Envelope'."
So I tried declaring the path like this:
declare variable $m := "/soap:Envelope/soap:Body/ns2:runSearchResponse/return";
for $x in doc("2002test.xml")//$m
return $x
Which got me:
/soap:Envelope/soap:Body/ns2:runSearchResponse/return
/soap:Envelope/soap:Body/ns2:runSearchResponse/return
/soap:Envelope/soap:Body/ns2:runSearchResponse/return
...
I tried:
for $x in doc("2002test.xml")/*[local-name()='soap:Envelope'][local-name()='soap:Body'][local-name()='ns2:runSearchResponse'][local-name()='return']
return $x
Which did nothing.
Very simply I'd like to be able to do something like this:
for $x in doc("2002test.xml")
where $x/return/_items/_itemFields/itemFields/fieldValue/value="default_ticket.png"
return $x/return/_items/_itemFields/itemFields/fieldName/text()
resulting in:
Icon Name
Any advice?
Use basic XPath expressions with a predicate e.g.
doc("2002test.xml")//itemFields[fieldValue/value = "default_ticket.png"]/fieldName/text()
If you think you need a FLOWR expression then use e.g.
for $field in doc("2002test.xml")//itemFields
where $field/fieldValue/value = "default_ticket.png"
return $field/fieldName/text()
Sample text file contains:
`<?xml version="1.0" encoding="UTF-8" ?>
<Document xmlns ="urn:iso:std:iso:20022:tech:xsd:camt.056.001.01"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<FIToFIPmtCxlReq>
<Assgnmt>
<Id>ID123456</Id>
<Assgnr>
<Agt>
<FinInstnId>
<BIC>BICSEND</BIC>
</FinInstnId>
</Agt>
</Assgnr>
<Assgne>
<Agt>
<FinInstnId>
<BIC>BICRCV</BIC>
</FinInstnId>
</Agt>
</Assgne>
<CreDtTm>2020-12-16T09:05:15.0Z</CreDtTm>
</Assgnmt>
<CtrlData>
<NbOfTxs>1</NbOfTxs>
<CtrlSum>0</CtrlSum>
</CtrlData>
<Undrlyg>
<TxInf>
<CxlId>20201216.105.19344855940590400</CxlId>
<OrgnlGrpInf>
<OrgnlMsgId>REF123456789</OrgnlMsgId>
<OrgnlMsgNmId>pacs.008</OrgnlMsgNmId>
</OrgnlGrpInf>
<OrgnlInstrId>FT123456</OrgnlInstrId>
<OrgnlEndToEndId>NOTPROVIDED</OrgnlEndToEndId>
<OrgnlTxId>20201216.100.02202020</OrgnlTxId>
<OrgnlIntrBkSttlmAmt Ccy="EUR">25.23</OrgnlIntrBkSttlmAmt>
<OrgnlIntrBkSttlmDt>2020-12-16</OrgnlIntrBkSttlmDt>`
Please be informed that I would like to code PowerShell to extract the data in tag <OrgnlIntrBkSttlmAmt> (please note that the data length can change since this is an amount field) and then replace the "0" in tag <CtrlSum> with "25.23".
Can someone help me with this.
Thank you for your time.
The xml you show us is invalid as it is missing the following closing tags:
</TxInf>
</Undrlyg>
</FIToFIPmtCxlReq>
</Document>
If I add these, you could do this to update the value in the <CtrlSum> tag:
# load the xml from file
[xml]$xml = Get-Content -Path 'D:\Test\test.xml' -Raw
# get the amount from the 'OrgnlIntrBkSttlmAmt' tag
$amount = $xml.Document.FIToFIPmtCxlReq.Undrlyg.TxInf.OrgnlIntrBkSttlmAmt.'#text'
# use that amount to put in the 'CtrlSum' tag
$xml.Document.FIToFIPmtCxlReq.CtrlData.CtrlSum = $amount
# save the updated xml to file
$xml.Save('D:\Test\test.xml')
I am having 2 diffrent xml file with. Currently im trying to get the Last xml node. In the 1st ouput im getting because empty or Null value since i have a commented line. Please help how to handle in this case.
#Below line Run firstfile.xml
$xmlfile = 'C:\Programs\MyTasks\TCPROD-29658\Test_1309\D_SimulationTest\firstfile.xml'
#Below line to Run secondfile.xml
#$xmlfile = 'C:\Programs\MyTasks\TCPROD-29658\Test_1309\D_SimulationTest\secondfile.xml'
$xmllastsitenode = [xml]::new()
$xmllastsitenode.Load($xmlFile)
$lastsite_id = $xmllastsitenode.fccconfig.fccdefaults.LastChild.id
write-host 'Print1'
write-host $lastsite_id
if (!$lastsite_id) { Write-Host "variable is null" }
if ($lastsite_id) { Write-Host "variable is NOT null" }
write-host 'Print2'
Below is the xml files im trying to run
1st XML file
<?xml version="1.0" encoding="UTF-8"?>
<fccconfig version="1.3.2">
<fccdefaults>
<property name="CacheLocation" value="C:/Users/Public/" overridable="true"/>
<site id="-1940805554" overridable="true">
<parentfsc address="http://abcdefgh:1234/" priority="0" />
</site>
<!--__ANT_MARK__-->
</fccdefaults>
<!-- default parentfsc - this is a marker that will be overwritten by the installer -->
<parentfsc address="xyzlmnopq:10010" priority="0" transport="lan"/>
</fccconfig>
Output:-
Print1
variable is null
Print2
2nd XML file
<?xml version="1.0" encoding="UTF-8"?>
<fccconfig version="1.3.2">
<fccdefaults>
<property name="CacheLocation" value="C:/Users/Public/" overridable="true"/>
<site id="-1940805554" overridable="true">
<parentfsc address="http://abcdefgh:1234/" priority="0" />
</site>
</fccdefaults>
<!-- default parentfsc - this is a marker that will be overwritten by the installer -->
<parentfsc address="xyzlmnopq:10010" priority="0" transport="lan"/>
</fccconfig>
Output:-
Print1
-1940805554
variable is NOT null
Print2
Latest Code which have tried meantime
#Below line Run firstfile.xml
$xmlfile = 'C:\Programs\MyTasks\TCPROD-29658\Test_1309\D_SimulationTest\firstfile.xml'
#Below line to Run secondfile.xml
#$xmlfile = 'C:\Programs\MyTasks\TCPROD-29658\Test_1309\D_SimulationTest\secondfile.xml'
$xmllastsitenode = [xml]::new()
$xmllastsitenode.Load($xmlFile)
$lastsite_id = $xmllastsitenode.fccconfig.fccdefaults.LastChild.id
write-host 'Print1'
write-host $lastsite_id
if (!$lastsite_id)
{
Write-Host "variable is null"
# New logic to handle firt xml file cases
#$somesitetag ---> Having some xml tag content here
$newSiteNode = $xmlcontent.ImportNode($somesitetag, $true)
$antmarkline = (Get-Content $xmlfile) | select-string -Pattern $antmark
$xmlcontent.fccconfig.fccdefaults.InsertAfter($newSiteNode,$antmarkline)
}
if ($lastsite_id)
{
Write-Host "variable is NOT null"
#Insterting some new nodes
#This Working as second file conetent works here
}
write-host 'Print2'
You need to ignore the comments. Check the output of this for the first XML file.
$settings = [System.Xml.XmlReaderSettings]::new()
$settings.IgnoreComments = $true
$xmlreader = [System.Xml.XmlReader]::Create('./Desktop/Test.xml', $settings)
$xmldoc = [System.Xml.XmlDocument]::new()
$xmldoc.Load($xmlreader)
$xmldoc.fccconfig.fccdefaults.LastChild
I have made a function for creating new xml node.there are two parameters in my function on is a existing xml file reference and second one is element value.while running the script its showing an error
code
function createProviderNode($xmlData,$propertyValue){
Write-Host 'inside createProviderNode'
Write-Host ($propertyValue)
#[xml]$xmlData = get-content E:\powershell\data.xml
$newProviderNode = $xmlData.CreateNode("element","provider","")
$newProviderNode.SetAttribute("name",$propertyValue)
$xmlData.SelectSingleNode('providers').AppendChild($newProviderNode)
$xmlData.save("E:\powershell\data.xml")
}
did i miss anything in this code?
The error message implies that while you expected $xmlData to contain an object of type [xml] (System.Xml.XmlDocument) - i.e., an XML document - in reality it was a string ([string]).
In other words: When you called your createProviderNode function, the 1st argument you passed was a string, not an XML document (of type [xml]).
Typing your $xmlData parameter variable as [xml] solves this problem, as that will implicitly covert even a string argument to an XML document on demand - if possible.
A simplified example, using a script block in lieu of a function:
$xmlString = #'
<?xml version="1.0"?><catalog><book id="bk101"><title>De Profundis</title></book></catalog>
'#
# Note how $xmlData is [xml]-typed.
& { param([xml] $xmlData) $xmlData.catalog.book.title } $xmlString
The above yields De Profundis, demonstrating that the string argument was converted to an [xml] instance (which - thanks to PowerShell's type adaptation magic - makes the element names available as direct properties).
It is then safe to call the .CreateNode() method on $xmlData.
Well, you don't show your original XML format.
Why did you comment out that Get-Content? it will not work without it.
So, if we take the below example, it works as expected.
# Simple XML version
$SimpleXml = $null
$SimpleXml = #"
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<name>Apple</name>
<size>1234</size>
</configuration>
"#
# New node code
[xml]$XmlDoc = Get-Content -Path variable:\SimpleXml
$runtime = $XmlDoc.CreateNode("element","runtime","")
$generated = $XmlDoc.CreateNode("element","generatePublisherEvidence","")
$generated.SetAttribute("enabled","false")
$runtime.AppendChild($generated)
$XmlDoc.configuration.AppendChild($runtime)
$XmlDoc.save("$pwd\SimpleXml.xml")
Get-Content -Path "$pwd\SimpleXml.xml"
# Which creates this:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<name>Apple</name>
<size>1234</size>
<runtime>
<generatePublisherEvidence enabled="false" />
</runtime>
</configuration>
Also Write-Host is never needed unless you are coloring screen output.
Write-Output is the default and automatically write to the screen, whether you specify Write-Output or not.
So, all of these to the same thing - output to the screen.
$SomeString = 'hello'
Write-Host $SomeString
Write-Output $SomeString
'hello'
"hello"
$SomeString
"$SomeString"
($SomeString)
("$SomeString")
$($SomeString)
# Results
hello
hello
hello
hello
hello
hello
hello
… yet, it's your choice.
I have a script that reads from an XML file, performs some web services, from which it gets a response and I'd now like the XML fie to be amended with the webs response as below.
Original XML:
<ItemNum>
<Key>11233</Key>
<Item>123.jpg</Item>
<Item>456.jpg</Item>
<Item>789.jpg</Item>
<Detail>Processed on 8-12-2017</Detail>
</ItemNum>
The script will process the items from the XML and iterate through the itemlist, similar to below:
[xml]$xml = Get-Content -Path $xmlPath
Foreach ($websResponse in $websResponseList) {
<Create New Element>
<Add Webs Response to Element>
}
$xml.save($xmlPath)
The result should be the original XML modified to look like the below:
<ItemNum>
<Key>11233</Key>
<Item>123.jpg</Item>
<Item>456.jpg</Item>
<Item>789.jpg</Item>
<Detail>Processed on 8-12-2017</Detail>
<WebS>00001</WebS>
<WebS>00002</WebS>
<WebS>00003</WebS>
</ItemNum>
I've read countless articles on working with XML in Powershell and probably some that are very similar what I'm trying to achieve but this is for some reason stumping me so would appreciate any pointers.
You're almost there:
[xml]$xml = Get-Content -Path $xmlPath
foreach ($websResponse in $websResponseList)
{
$elem = $xml.CreateElement('WebS','00001') # substitute with data
$xml.ItemNum.AppendChild($elem)
}
$xml.save($xmlPath)