How to remove all XML children nodes but not attributes in Powershell - powershell

From this :
<products id="col">
<product code="id1" />
<product code="id2" />
</products>
I want to get this :
<products id="col">
</products>
If I use RemoveAll() it will remove products id attribute also so I will get this
<products>
</products>
That's not what I want. What method could I use to keep my id attribute in products node ?

Instead of calling RemoveAll(), you need to find the nodes you want to remove and then remove each of them manually, with RemoveChild():
$XmlDocument = [xml]#'
<products id="col">
<product code="id1" />
<product code="id2" />
</products>
'#
$ChildNodes = $XmlDocument.SelectNodes('//product')
foreach($Child in $ChildNodes){
[void]$Child.ParentNode.RemoveChild($Child)
}
$XmlDocument.Save("C:\path\to\doc.xml")
If you don't know the name of the immediate child nodes, but only the parent node name, you can use XPath to select them anyways: '//ParentNodeName/child::*'
$ChildNodes = $XmlDocument.SelectNodes('//products/child::*')

My answer doesn't rely on XPath to accomplish this. The script assumes $XmlDocument is a variable containing an XmlDocument instance. The script also assumes that only <product> elements live inside <products>.
if ($XmlDocument.products -is [Xml.XmlElement]) {
$XmlDocument.products.product | %{ $_.ParentNode.RemoveChild($_) | Out-Null }
}
This code also works if <products> does not contain any child elements (i.e., it is a text element).

Related

How to write dynamic attributes

I need to write a dynamic Attribute name instead of hardcode of Name attribute in dataweave 2.0 mulesoft 4 in anypointstudio
<?xml version="1.0" encoding="UTF-8"?>
<iGoApplicationData>
<UserData>
<Data Name="UpdateUserProfile">True</Data>
<Data Name="Action">??</Data>
</iGoApplicationData>
So in order to generate an XML like yours the DW structure will look like
{
iGoApplicationData: {
UserData: {
Data #(Name: payload.foo): "True",
Data #((var.attributeName): "Action"): "??"
}
}
}
So in this example I show how to specify a value in the attribute or a dynamic attribute name. For the dynamic attribute value just type the expression on the value side of the attribute (the part that goes after the :)
For dynamic attribute name you need to wrap the expression between parenthesis. When the name is wrapped between parenthesis it is considered dynamic. This applies to object keys and attributes names

Is it possible to NATVIS a recursive tuple (variadic template)?

I implemented the tuple from here: https://voidnish.wordpress.com/2013/07/13/tuple-implementation-via-variadic-templates/
Is it possible to visualize it with NATVIS? I got as far as
<Type Name="tuple">
<DisplayString>()</DisplayString>
</Type>
<Type Name="tuple<*>">
<DisplayString>({_Myfirst})</DisplayString>
</Type>
How can I get the _Myfirst value for more than one type, to get
<Type Name="tuple<*,*>">
<DisplayString>({_Myfirst}, {???})</DisplayString>
</Type>
<Type Name="tuple<*,*,*>">
<DisplayString>({_Myfirst}, {???}, {???})</DisplayString>
</Type>
etc?
You'll have to modify the type a little to get this to work. What's required is a base_type typedef.
i.e.
// tuple
template<class... _Types> class tuple;
// empty tuple
template<> class tuple<> {};
// recursive tuple definition
template<class _This,
class... _Rest>
class tuple<_This, _Rest...>
: private tuple<_Rest...>
{
public:
typedef tuple<_Rest...> base_type; // ***** Added this line
_This _Myfirst;
};
Now we can recursively evaluate the base types with the natvis declarations:
<!-- Handle empty tuples -->
<Type Name="tuple<>">
<DisplayString>()</DisplayString>
<Expand/>
</Type>
<!-- Handle a single parameter (this is also our terminator for recursion) -->
<Type Name="tuple<*>">
<DisplayString IncludeView="noparens">{_Myfirst}</DisplayString>
<DisplayString ExcludeView="noparens">({_Myfirst})</DisplayString>
<Expand>
<Item Name="Value">_Myfirst</Item>
</Expand>
</Type>
<!-- Handle 2 or more items -->
<Type Name="tuple<*,*>">
<!-- show the first item and then recurse by casting this to 'base_type' -->
<DisplayString IncludeView="noparens">{_Myfirst}, {*(base_type *)this,view(noparens)}</DisplayString>
<!-- Wrap our display string that doesn't a have any parentheses, this will be only done for the top level tuple -->
<DisplayString ExcludeView="noparens">({*this,view(noparens)})</DisplayString>
<Expand>
<!-- Show the top level item -->
<Item Name="Value">_Myfirst</Item>
<!-- Recursively expand our base types -->
<ExpandedItem>*(base_type *)this</ExpandedItem>
</Expand>
</Type>
And this is the result:

Yahoo GeoPlanet - find places in specified country or woeid

I'm trying to make sure the result from text search is only UK or Ireland. My places REST request looks like this:
http://where.yahooapis.com/v1/places.q('Belfast')?format=json&appid=MyAppIdGoesHere
Is there another parameter for the URL that I can specify the country or woeid the result must be in?
I don't believe there is a way to add that specificity within the GeoPlanet web service call. There is support for Filters, including the $and filter which lets you combine two filters together, but it doesn't look like a combination exists that would satisfy your request directly.
As an alternative, you could make the API call, then filter the results to keep only those results in the matching country.
For example, the following is part of the results for a search for "Belfast". After retrieving these results, you could keep only those places where country=GB:
<place xmlns="http://where.yahooapis.com/v1/schema.rng"
xml:lang="en-GB" yahoo:uri="http://where.yahooapis.com/v1/place/44544">
<woeid>44544</woeid>
<placeTypeName code="7">Town</placeTypeName>
<name>Belfast</name>
<country code="GB" type="Country" woeid="23424975">United Kingdom</country>
<admin1 code="GB-NIR" type="Country" woeid="20070563">Northern Ireland</admin1>
<admin2 code="GB-BFS" type="County" woeid="20071112">Belfast</admin2>
<admin3 code="" type="Local Administrative Area" woeid="20078326">Belfast</admin3>
<locality1 type="Town" woeid="44544">Belfast</locality1>
</place>
<place xmlns="http://where.yahooapis.com/v1/schema.rng"
xml:lang="en-GB" yahoo:uri="http://where.yahooapis.com/v1/place/2361609">
<woeid>2361609</woeid>
<placeTypeName code="7">Town</placeTypeName>
<name>Belfast</name>
<country code="US" type="Country" woeid="23424977">United States</country>
<admin1 code="US-ME" type="State" woeid="2347578">Maine</admin1>
<admin2 code="" type="County" woeid="12588673">Waldo</admin2>
<admin3/>
<locality1 type="Town" woeid="2361609">Belfast</locality1>
</place>
<place xmlns="http://where.yahooapis.com/v1/schema.rng"
xml:lang="en-GB" yahoo:uri="http://where.yahooapis.com/v1/place/2348154">
<woeid>2348154</woeid>
<placeTypeName code="7">Town</placeTypeName>
<name>Belfast</name>
<country code="NZ" type="Country" woeid="23424916">New Zealand</country>
<admin1 code="NZ-CAN" type="Region" woeid="15021751">Canterbury</admin1>
<admin2 code="" type="County" woeid="55875854">Christchurch City</admin2>
<admin3/>
<locality1 type="Town" woeid="2348154">Belfast</locality1>
</place>
<place xmlns="http://where.yahooapis.com/v1/schema.rng"
xml:lang="en-GB" yahoo:uri="http://where.yahooapis.com/v1/place/2361600">
<woeid>2361600</woeid>
<placeTypeName code="7">Town</placeTypeName>
<name>Belfast</name>
<country code="US" type="Country" woeid="23424977">United States</country>
<admin1 code="US-NY" type="State" woeid="2347591">New York</admin1>
<admin2 code="" type="County" woeid="12589313">Allegany</admin2>
<admin3/>
<locality1 type="Town" woeid="2361600">Belfast</locality1>
</place>
This can be made possible by using the $and operator as your filter. Take note that the $and operator can combine two different filters, which is in your case you want to do the text search within a specified place (UK or Ireland). For that you can use the .woeid and the .q filters like so:
http://where.yahooapis.com/v1/places$and(.woeid(23424975),.q('Belfast'))?format=json&appid=YourAppIdGoesHere
wherein in this example, the numeric value 23424975 is the woeid of UK.

How to select siblings (xpath syntax) with Perl's XML::Twig?

I need to select the next node via next_sibling or first_elt. But I want to filter by node name (containing the string "TON")
first_elt ('HILTON[#method]' or 'SHERATON[#method]');
or
next_sibling ('HILTON[#method]' or 'SHERATON[#method]');
or
next_sibling ('TON[#method]');
Example I tried (not working):
#!/usr/bin/perl -w
use warnings;
use XML::Twig;
$t-> parsefile ('file.xml');
my $Y0=$t->first_elt('HILTON[#method]' or 'SHERATON[#method]');
it will just process for 'HILTON[#method]'
my $Y0=$t->first_elt('/*TON[#method]');
wrong navigation condition '/*TON[#method]' () at C:/strawberry/perl/site/lib/XML/Twig.pm line 3523
As this is outside of the XPath subset supported by XML::Twig, you have to use a custom filter, by passing code to first_elt:
$t->first_elt( sub { $_[0]->tag=~ m{TON$} && $_[0]->att( 'method') })
This returns the first element for which the sub returns a true value.
The need for such an expression is a bit troubling though. In your example you define a class of elements by the fact that their name ends in TON. What happens when you have a CARLTON element? Or when MARRIOTT elements need to be processed with SHERATON and HILTON? Do you need to rewrite your queries?
If you are the one designing the format of the data, I would suggest revising the format. HILTON and SHERATON should probably be attributes of a HOTEL, BRAND or OWNER tag. Maybe an additional attribute would be useful, to mark that both types should be processed similarly. This attribute would only make sense if it is a property intrinsic to the data.
If the data is what it is and you have no input on its format, then I would have a list of the tags to process and check on these:
my %TAGS_TO_PROCESS= map { $_ => 1 } qw( HILTON SHERATON);
my $elt= $t->first_elt( sub { $TAGS_TO_PROCESS{$_[0]->tag} && $_[0]->att( 'method') })
This way adding/substracting other tags is easy.
Use:
*[substring(name(), string-length(name()) - 2) = 'TON'][#method][1]
Explanation:
This expression uses an XPath 1.0 equevalent for the XPath 2.0 standard function ends-with():
The XPath 1.0 equivalent of the XPath 2.0 expression:
ends-with($s, $s2)
is:
substring($s, string-lenth() - string-length($s2) + 1) = $s2
In this last expression we substitute $s with name() and $s2 with 'TON'
XSLT - based verification:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:copy-of select=
"*[substring(name(), string-length(name()) - 2) = 'TON'][#method] "/>
==========
<xsl:copy-of select=
"*[substring(name(), string-length(name()) - 2) = 'TON'][#method][1] "/>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<t>
<HILTON method="buy"/>
<TON method="burn"/>
<TONIC method="drink"/>
<HILTON nomethod="yes"/>
<SHERATON/>
<SHERATON method="visit"/>
</t>
the transformation evaluates the two XPath expressions and the selected nodes are copied to the output:
<HILTON method="buy"/>
<TON method="burn"/>
<SHERATON method="visit"/>
==========
<HILTON method="buy"/>
The first expression selects all elements - children of the context node, whose name ends with "TON" and that also have a method attribute.
The second expression selects the first node from those, selected by the first expression.

How do I write multiple items with Zend_Config_Writer_Xml?

I am trying to write a XML file with Zend_Config_Writer_Xml. I found out a problem that I can't write multiple items under a root. I would like to do,
<root>
<item name="test"></item>
<item name="test2"></item>
</root>
I can't find a method to do this on zend documentation.
Please advise me.
Standard writer doesn't do exactly that, but it works like this: if you do:
$config = new Zend_Config(array(), true);
$config->root = array("test1" => 1, "test2" => array(1,2));
$writer = new Zend_Config_Writer_Xml();
$writer->write('config.xml', $config);
then what you get is:
<zend-config xmlns:zf="http://framework.zend.com/xml/zend-config-xml/1.0/">
<root>
<test1>1</test1>
<test2>1</test2>
<test2>2</test2>
</root>
</zend-config>
I don't think standard writer does attributes, you'd need to override it for that.