How can I remove substring from the main string using Powershell script? - powershell

My powershell code actually reads data from an XML and stores the specific data to a csv file.
The XML file somewhat looks like below:
<?xml version="1.0" encoding="utf-8"?>
<Report Version="10.0">
<Targets>
<Target Name="\\bin\testBusiness.dll">
<Modules>
<Module Name="testing.dll" AssemblyVersion="1.0.1003.312" FileVersion="1.0.0.0">
<Metrics>
<Metric Name="Maintainability" Value="78" />
</Metrics>
</Module>
</Modules>
</Target>
</Targets>
</Report>
I need to extract only the "testing.dll" from the above XML code. The code I am using to do so is as below:
$testDLL = [regex]::matches($xmlfile[5], '<mod name=".*" ')[0].value -replace '<mod name="(.*)" ','$1'
#the above code line gets even the AssemblyVersion
$testAssembver = [regex]::matches($xmlfile[5], 'AssemblyVersion=".*" ')[0].value -replace 'AssemblyVersion=".*" ','$1'
I don't need AssemblyVersion to be concatenated to the "testing.dll (mod name in xml)" from the XML code.
Currently I get something like:
testing.dll" AssemblyVersion=1.0.1000.112"
I just need testing.dll, everything thereafter should be ommitted.
Please help.
Thanks,
Ashish

I don't think that regular expression is the best way to parse XML. Perhaps ou'd better use XMLDocument.
Using a better XML document :
<Dumy>
<Report Version="10.0"></Report>
<Goals>
<Name>"\\somepath\path.dll"</Name>
<Mods>
<Mod Name="testing.dll" AssemblyVersion="1.0.1000.112" FileVersion="1.0.0.1"></Mod>
</Mods>
</Goals>
</Dumy>
You can find your data like this :
$data = [XML](Get-Content "C:\temp\test.xml")
$data.Dumy.Goals.Mods.Mod.Name

Use XML as correctly suggested by JPBlanc. Then use XPath. Example, to extract the wanted value:
$data.selectnodes("//Modules/Module/#Name")

Related

SelectSingleNode returns a null value

I am trying to select and remove a single node from an XML document. Sample code and XML below:
[xml]$xml = Get-Content MyXml.xml
$xml.MigrationTable.Mapping[1].SelectSingleNode("DestinationSameAsSource")
This currently returns nothing. This answer shows a C# example for including the namespace when calling the SelectSingleNode() method.
How can I include the namespace with SelectSingleNode() in PowerShell?
XML:
<?xml version="1.0" encoding="UTF-16"?>
<MigrationTable xmlns="http://microsoft.com/GroupPolicy/GPOOperations/MigrationTable" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Mapping>
<Type>LocalGroup</Type>
<Source>Group1#Contoso.local</Source>
<Destination>Group2#contoso.local</Destination>
</Mapping>
<Mapping>
<Type>Unknown</Type>
<Source>Network Service</Source>
<DestinationSameAsSource/>
</Mapping>
<Mapping>
<Type>Unknown</Type>
<Source>Local Service</Source>
<DestinationSameAsSource/>
</Mapping>
</MigrationTable>
The Powershell syntax for NameTable handling is pretty similar to C#. After loading the data, create a NamespaceManager based on the XML document.
To select elements, a dummy namespace prefix needs to be used, in this sample x is added and used in Xpath. Like so,
[xml]$xml = Get-Content MyXml.xml
$nsmgr = new-object Xml.XmlNamespaceManager($xml.NameTable)
$nsmgr.AddNameSpace("x", "http://microsoft.com/GroupPolicy/GPOOperations/MigrationTable")
$xml.MigrationTable.Mapping[1].SelectSingleNode("x:DestinationSameAsSource", $nsmgr)

Accessing DOM element with namespace in Freemarker template

I have the following XML
<wmi xmlns="http://www.exmple.com/XMLSchema/fulfillment/v1/order/orderShipment" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.com/XMLSchema/fulfillment/v1/order/orderShipment OrderShipmentNotification.xsd">
<wmiHeader>
<fileID>693401.20160229.130342.3541254</fileID>
<version>2.0.0</version>
<messageType>FSN</messageType>
<genDate>2016-02-29T13:03:42Z</genDate>
<from>
</from>
</wmiHeader>
<orderShipNotification>
<shipmentHeader dateTimeCreated="2016-02-29T13:03:42Z" requestNumber="2574445351883" />
<shipmentDetails actualShipmentDateTime="2016-02-29T12:18:54Z" carrierCode="XX" carrierMethodCode="XX-01">
<shipmentPackDetails trackingNumber="9361289672090007124848" trackingURL="https://example.com/go/TrackConfirmAction_input?qtc_tLabels1=323434">
<shipmentPackLineDetails requestLineNumber="1" partnerItemID="FXT-CC-LB" itemQtyShipped="1" />
</shipmentPackDetails>
</shipmentDetails>
</orderShipNotification>
</wmi>
I am getting error in Freemarker template when I am trying to access.
${orderShipNotification.shipmentDetails.#actualShipmentDateTime[0]!""}
If I delete the namespaces from the document it is working fine. I deleted the following content from the XML
xmlns="http://www.exmple.com/XMLSchema/fulfillment/v1/order/orderShipment" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.com/XMLSchema/fulfillment/v1/order/orderShipment OrderShipmentNotification.xsd"
I did some investigation. The is a ftl directive. But it is still not clear how this will solve the problem. Please let me know how I can access the attributes.
http://freemarker.incubator.apache.org/docs/ref_directive_ftl.html#ref.directive.ftl
Start the template with
<#ftl ns_prefixes={"D":"http://www.exmple.com/XMLSchema/fulfillment/v1/order/orderShipment"}>
This sets the namespace as the default (D stands for default). Note that if you will also use XPath queries, there you will have to write out the D: before the element names (this is an XPath restriction).
This is documented here: http://freemarker.org/docs/xgui_imperative_learn.html

Perl XML::LibXML - Cannot replace Replace Element value

The XML file looks like this
<?xml version="1.0"?>
<application name="pos">
<artifact id="example.war" type="war" cycle="ReleaseX-Sprint1">
<jira>tick-1,tick-2,</jira>
<jenkins>http://localhost:0000/hudson</jenkins>
<kportal/>
<scm>
<transaction id="111" user="user1">
<file name="a/b/c/d.txt"/>
<file name="x/y/z.xml"/>
</transaction>
</scm>
</artifact>
</application>
I want to add a value to the kportal node for a particular artifact node
so that it looks like <kportal>KPORTAL-1</kportal>
My code looks like this
my $manifestDoc = $manifestFileParser->parse_file($manifestFile);
my $xpathKportal = qq(//application[\#name="$applicationName"]/artifact[\#id="$artifactID"]/kportal);
my $newdeploymentNode = $manifestDoc->findnodes($xpathKportal);
$newdeploymentNode->removeChildNodes();
$newdeploymentNode->appendText('KPORTAL-1');
I am getting the error
Can't locate object method "removeChildNodes" via package "XML::LibXML::NodeList"
XML::LibXML::Node::findnodes returns a NodeList object in scalar context, but a list of nodes in list context. The NodeList does not have a removeChildNodes method. Suggested solution: Use a list on the left side of = to force list context.
my ($new_deployment_node) = $manifest_doc->findnodes(...);
There are two possbile errors:
You have a typo: $newdeploymentNode ->removeChildNodes();
and maybe your are not using the latest version from XML::LibXML and in this version this method call is not supported.

XDT Transform: InsertBefore - Locator Condition is ignored

I have a web.config file in which I need to either insert the <configSections /> element or manipulate children of that node if it already exists.
If it already exists I don't want to insert it again (obviously, as it is only allowed to exist once).
Normally, that would not be a problem, however:
If this element is in a configuration file, it must be the first child element of the element.
Source: MSDN.
So if I use xdt:Transform="InsertIfMissing" the <configSections /> element will always be inserted after any existing child elements (and there are always some), violating the above restriction of it having to be the first child element of <configuration />
I attempted to make this work in the following way:
<configSections
xdt:Transform="InsertBefore(/configuration/*[1])"
xdt:Locator="Condition(not(.))" />
Which works perfect, if the <configSections /> element doesn't already exist. However, the condition I've specified seems to be ignored.
In fact, I've tried a few conditions like:
Condition(not(/configuration[configSections]))
Condition(/configuration[configSections] = false())
Condition(not(/configuration/configSections))
Condition(/configuration/configSections = false())
Finally, out of desperation, I tried:
Condition(true() = false())
It still inserted the <configSections /> element.
It is important to note that I'm trying to include this in a NuGet package, so I will be unable to use a custom transform (like the one AppHarbor uses).
Is there any other clever way to get my element in the right place only if it doesn't yet exist?
To test this out, use AppHarbors config transform tester. Replace the Web.config with the following:
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="initialSection" />
</configSections>
</configuration>
And Web.Debug.config with the following:
<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<configSections
xdt:Transform="InsertBefore(/configuration/*[1])"
xdt:Locator="Condition(true() = false())" />
<configSections>
<section name="mySection" xdt:Transform="Insert" />
</configSections>
</configuration>
The result will show two <configSections /> elements, the one containing "mySection" being the first, as specified in the InsertBefore Transform.
Why was the Locator Condition not taken into account?
So after facing the same issue, I came up with a solution. It's not pretty nor elegant, but it works. (At least on my machine)
I just split the logic into 3 different statements. First, I add an empty configSections at the correct position (first). Then I insert the new config to the last configSections, which would be the new one if it is the only one, or a previously existing one otherwise.
Lastly I remove any empty configSections elemnt which might exist. I'm using RemoveAll for no good reason, you should probably use Remove.
The overall code looks like so:
<configSections xdt:Transform="InsertBefore(/configuration/*[1])" />
<configSections xdt:Locator="XPath(/configuration/configSections[last()])">
<section name="initialSection" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
</configSections>
<configSections xdt:Transform="RemoveAll" xdt:Locator="Condition(count(*)=0)" />
The question which still remains unanswered is why Locator conditions are not taken into account for InsertBefore. Or why I can't handle an empty match set for InsertBefore, because that would allowed me to do fun things such as
//configuration/*[position()=1 and not(local-name()='configSections')]
Which to be honest is a much clearer way of doing what I want to achieve.

how to extract xml tag values from email body?

I know the process to extract the body from the email using MIME::PARSER, but in my mail i have xml data. whats is the process to extract xml tag values from the email body?
Body:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<TEST>
<test>test</test>
<test1>test1</test1>
</TEST>
Assuming the body actually consists of XML, just feed it into your XML parser of choice (XML::Twig seems popular these days or you could look at Task::Kensho's recommended XML modules) and use it as normal.