Powershell XML content manipulation - regex

I have an XML file with several entries using same keys and values handling credentials with PowerShell, constructed by Explort-Clixml. An example:
<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
<Obj RefId="0">
<TN RefId="0">
<T>System.Collections.Hashtable</T>
<T>System.Object</T>
</TN>
<DCT>
<En>
<S N="Key">fabrikam</S>
<Obj N="Value" RefId="1">
<TN RefId="1">
<T>System.Management.Automation.PSCredential</T>
<T>System.Object</T>
</TN>
<Props>
<S N="UserName">admin#fabrikam.com</S>
<SS N="Password">01000000...</SS>
</Props>
</Obj>
</En>
<En>
<S N="Key">contoso</S>
<Obj N="Value" RefId="2">
<TNRef RefId="1" />
<Props>
<S N="UserName">admin#contoso.com</S>
<SS N="Password">01000000...</SS>
</Props>
</Obj>
</En>
<En>
<S N="Key">adatum</S>
<Obj N="Value" RefId="3">
<TNRef RefId="1" />
<Props>
<S N="UserName">admin#adatum.com</S>
<SS N="Password">01000000...</SS>
</Props>
</Obj>
</En>
</DCT>
</Obj>
</Objs>
The same Export-Clixml handles adding new entries to the list (as hashtables), but I'm struggling when I'd have to modify entries I already have.
1) If I specify to delete an item named contoso, what is the best way to seek, select and remove everything from that <En> entirely?
$xml = [xml](Get-Content file.xml)
$xml.SelectNodes("//Objs/Obj/DCT/En")
... yields nothing at all, whereas
$xml.Objs.Obj.DCT.En.s
works perfect and returns the list of entries by their name. I would need to remove an entire <En> element per given value for <S N="Key">...</S>. Trying to catch the correct entry with
$xml.Objs.Obj.DCT.En.ChildNodes | ? { '#text' -contains 'contoso' }
returns nothing.
2) RefId value in Obj is an auto incremented number. If I remove contoso between the other two entries, what would be the best way to seek and replace the value of RefId in fabrikam and adatum so they're in order (1,2) again? A noteworthy point here is that only the first <En> in the list has sub-elements for <TN RefId> element, the others do not.

Related

rapidxml weird parsing for one specific node

I've been looking for hours a solution to my (simple ?) problem but I cannot find anyone who encountered this. I'm using latest version of rapidxml(1.13).
I'm currently trying to create a tile-based engine and I need to read tmx file.
I'm been using rapidxml for a while and so far everything was great. It was able to read every node perfectly and with an expected behavior. But I came across one node it has a problem with.
This is my tmx file :
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.5" tiledversion="1.6.0" orientation="orthogonal" renderorder="right-down" width="100" height="100" tilewidth="32" tileheight="32" infinite="0" nextlayerid="12" nextobjectid="1">
<tileset firstgid="1" source="../../../Tile_engine/Tile_engine/sprite/[Base]BaseChip_pipo.tsx"/>
<tileset firstgid="1065" source="../../../Tile_engine/Tile_engine/sprite/collision.tsx"/>
<layer id="4" name="background" width="100" height="100">
<properties>
<property name="bg" type="bool" value="false"/>
</properties>
<data encoding="csv">
//I've removed the data for clearer view
</data>
</layer>
<layer id="6" name="object" width="100" height="100">
<properties>
<property name="isSolid" type="bool" value="true"/>
</properties>
<data encoding="csv">
//I've removed the data for clearer view
</data>
</layer>
<layer id="9" name="front" width="100" height="100">
<properties>
<property name="isSolid" type="bool" value="false"/>
</properties>
<data encoding="csv">
//I've removed the data for clearer view
</data>
</layer>
<layer id="11" name="collision" width="100" height="100">
<data encoding="csv">
//I've removed the data for clearer view
</data>
</layer>
</map>
In order to debug, I'm using a basic read with rapidxml :
xml_document<> doc;
xml_node<> * root_node;
// Read the xml file into a vector
ifstream theFile ("sprites/map_wtf.tmx");
vector<char> buffer((istreambuf_iterator<char>(theFile)), istreambuf_iterator<char>());
buffer.push_back('\0');
// Parse the buffer using the xml file parsing library into doc
doc.parse<0>(&buffer[0]);
// Find our root node
root_node = doc.first_node("map");
When I try to read (and count) the layer node for example :
int count_node(0);
for(xml_node<> * child = root_node->first_node("layer"); child != nullptr; child = child->next_sibling())
count_node++;
cout << count_node;
The output is correct and gives me 4.
But when I try to read the tileset node, the output gives me 6.
So I've assumed the behavior is link to the /> at the end of the tileset node (<tileset firstgid="1" source="../../../Tile_engine/Tile_engine/sprite/[Base]BaseChip_pipo.tsx"/>).
Since the nested property node has the same pattern (<property name="bg" type="bool" value="false"/>), I've tried this code :
int count_node(0);
for(xml_node<> * child = root_node->first_node("layer")->first_node("properties")->first_node("property"); child != nullptr; child = child->next_sibling())
count_node++;
cout << count_node;
who gives me the correct output aka : 1.
I've tried different parsing options for the line doc.parse<0>(&buffer[0]) but nothing works. I've also read the content of the buffer during theses tests and it was correct.
I must have the wrong way to read the file but I cannot understand why this script reads layer node and property node fine, but not the tileset ones.
Can anyone help me ?
Thanks !
The question is based on a misunderstanding about the result of
xml_node<> * child = root_node->first_node("layer");
child = child->next_sibling();
The child = child->next_sibling() returns the next node on the same level irregardless of the node name or any other constraint that was applied during the root_node->first_node("layer") selection.
However, the next_sibling signature is basically the same as the first_node signature, so it can be used to apply criteria to the next sibling node to be found.
So the (untested) approach would be like
int count_node(0);
for(xml_node<> * child = root_node->first_node("tileset"); child != nullptr; child = child->next_sibling("tileset"))
count_node++;
cout << count_node;

How to iterate a List and print it's content one by one in each groupFooter?

I just want to know, is there any way to iterate a List and print it's content one by one (incremental) in each groupFooter?
I create a group in my report, and in each groupFooter section, I want to display content from a java.util.List I sent from Java class via parameter.
Currently I just using jr:list and jr:listContents in my groupFooter, and the result is all contents from the list is printed in every groupFooter. I got an headache to solve this, so any help will relief me.
I don't think you should try to iterate the List to get content in a certain number of groupFooter, instead I would get content directly List based on index.
What we need is simple a pointer (position in list), in your case this number seems to be an incremented number every time we have a new group.
A variabile counting as group change is:
<variable name="countMyGroup" class="java.lang.Integer" incrementType="Group" incrementGroup="Group1" calculation="Count">
<variableExpression><![CDATA[""]]></variableExpression>
<initialValueExpression><![CDATA[0]]></initialValueExpression>
</variable>
With this simple variabile we will now have the current number of group that is displayed and we can add a textField with the value from or List using this number. see java.util.List.get(int index)
<parameter name="listOfStuff" class="java.util.List" isForPrompting="false"/>
.....
<textField>
<reportElement x="120" y="0" width="267" height="17" uuid="b45699d3-5d34-4d88-b7bc-2666cf787ace">
<printWhenExpression><![CDATA[$P{listOfStuff}.size()>=$V{countMyGroup}]]></printWhenExpression>
</reportElement>
<textFieldExpression><![CDATA[$P{listOfStuff}.get($V{countMyGroup}-1)]]></textFieldExpression>
</textField>
Note: the printWhenExpression will avoid IndexOutOfBoundsException, hence that the group number is higher then the size of our List
Full jrxml example, it use a dummy group changing on each record, to try it use a JREmptyDatasource with a couple of records.
<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="ListOnEachPage" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="33394f25-66fc-431b-ac82-88660e9115e5">
<property name="com.jaspersoft.studio.data.defaultdataadapter" value="Empty 4 records"/>
<parameter name="listOfStuff" class="java.util.List" isForPrompting="false">
<defaultValueExpression><![CDATA[Arrays.asList(new String[]{"group 1","group 2","group 3"})]]></defaultValueExpression>
</parameter>
<queryString>
<![CDATA[]]>
</queryString>
<variable name="countMyGroup" class="java.lang.Integer" incrementType="Group" incrementGroup="Group1" calculation="Count">
<variableExpression><![CDATA[""]]></variableExpression>
<initialValueExpression><![CDATA[0]]></initialValueExpression>
</variable>
<group name="Group1">
<groupExpression><![CDATA[$V{REPORT_COUNT}]]></groupExpression>
<groupFooter>
<band height="34">
<textField>
<reportElement x="120" y="0" width="267" height="17" uuid="b45699d3-5d34-4d88-b7bc-2666cf787ace">
<printWhenExpression><![CDATA[$P{listOfStuff}.size()>=$V{countMyGroup}]]></printWhenExpression>
</reportElement>
<textFieldExpression><![CDATA[$P{listOfStuff}.get($V{countMyGroup}-1)]]></textFieldExpression>
</textField>
<textField>
<reportElement x="445" y="0" width="100" height="17" uuid="e5c46332-b137-4c55-99e4-265c87b8f97d"/>
<textFieldExpression><![CDATA[$V{countMyGroup}]]></textFieldExpression>
</textField>
</band>
</groupFooter>
</group>
<detail>
<band height="63" splitType="Stretch">
<staticText>
<reportElement x="140" y="20" width="264" height="30" uuid="1ec9f950-dd2d-4c18-a81a-b0da937eb1b5"/>
<textElement>
<font size="14"/>
</textElement>
<text><![CDATA[Just some text to simulate detail band]]></text>
</staticText>
</band>
</detail>
</jasperReport>
Output, see how values from list are extracted as long as we are within size of List
You need to make a variable which increment at your group level.
Step 2 its to use that variable in a simple textfield or what component you want (not a collection component) and put his expression like this : list.indexOf(countVariable)

XSLT 1.0: selecting based on attribute of preceding siblings of current() node

I have a base xml file like:
<base>
...
<s l="something" />
<s l="somethingelse" />
<s>
...
</base>
In my xslt, I use the document() function on each <s> to pull in another file structured like:
<otherbase>
...
<r l="something" />
<r l="somethingelse" />
<r l="evenmore" />
...
</otherbase>
At <s> in the base file, I am trying to select all <r>s whose #l does not match any preceding-sibling::*/#l of the current <s>. Thus for the third <s> with no attribute, it would match <r l="evenmore" /> etc.
Something like:
<xsl:for-each select="document('mydoc.xml')//r[#l != preceding-sibling::*/#l]">
...
</xsl:for-each>
would work if I were matching within the <r> attributes I believe, but I need preceding-siblings of current() ... I haven't been able to figure out the syntax.
Unfortunately there are no 2.0 processors in my environment, stuck in xslt 1.0.
You need to use the current() function when you want to refer to the current s node within a predicate. Try:
select="document('mydoc.xml')//r[not(#l = current()/preceding-sibling::s/#l)]"
Note also the difference between $a != $b and not($a = $b).

How to trim the XSLT use attribute expression

I am new to XSLT and I have got a special requirement. My XML file looks like below.
<results>
<result>
<Price>
<LinePrices>
<ProductId>ProductId:1000</ProductId>
<Uom>KG</Uom>
<Quantity>0</Quantity>
</LinePrices>
<LinePrices>
<ProductId>ProductId:1002</ProductId>
<Uom>EACH</Uom>
<Quantity>0</Quantity>
</LinePrices>
</Price>
</result>
<result>
<productlist>
<productid>1000</productid>
<relevance>0.9</relevance>
<sponsored>0</sponsored>
</productlist>
<productlist>
<productid>1001</productid>
<relevance>0.8</relevance>
<sponsored>0</sponsored>
</productlist>
</result>
</results>
I have to write XSLT keys to match the pattern. I have written the key for productlist as shown below.
<xsl:key name="productsIdForProduct" match="productlist" use="productid" />
In the similar way I am trying to write the key for LinePrices as shown below.
<xsl:key name="productsIdPrice" match="result/LinePrices" use="ProductId" />
However this returns 'ProductId:1000' in use attribute. I am trying to trim 'ProductId:' from the use attribute value. How can I write a trim expression to extract '1000' from 'ProductId:1000' inside XPATH expression?
Just use substring-after():
substring-after(ProductId,':')

xsl:variable advanced select

I have several XML elements like this:
<testcase starttime="2012-09-03 10:41:29" timestamp=" 622.922000">
<testlogfile file="" />
<teststep timestamp=" 622.944000" level="0" type="user" ident="1" result="pass">Do something</teststep>
<teststep timestamp=" 622.965000" level="0" type="user" ident="2" result="pass">Do something</teststep>
<teststep timestamp=" 622.986000" level="0" type="user" ident="3" result="pass">Do something</teststep>
<verdict time="2012-09-03 10:41:30" timestamp=" 623.428000" endtime="2012-09-03 10:41:30" endtimestamp=" 623.428000" result="pass" />
<title>Title goes here</title>
<ident>TC100_06</ident>
<description>description goes here</description>
<extendedinfo type="test case status">Approved</extendedinfo>
<extendedinfo type="traceability">some requirement</extendedinfo>
<extendedinfo type="vehicle mode">Hibernate, Parked, Living, Accessory</extendedinfo>
<extendedinfo type="environment">Station with ATB</extendedinfo>
<extendedinfo type="variants">veh variants</extendedinfo>
</testcase>
I would like to make a xsl:variable select query to count testcases by "ident" and "test case status". I am able to implement 2 separate queries, but I dont know how to join them both:
<xsl:variable name="totalTc" select="count(//testcase[./ident!='text' and ./ident!='obsolete' and ./ident!='rtm' and ./ident!='status overview' and ./ident!='statistics'])"/>
<xsl:variable name="approvedTc" select="count(//extendedinfo[#type='test case status' and text()='Approved'])"/>
I expect joined query should look something like this, but I am unable to query attribute:
<xsl:variable name="totalTc" select="count(//testcase[./ident!='text' and ./ident!='obsolete' and ./ident!='rtm' and ./ident!='status overview' and ./ident!='statistics' and ./extendedinfo=='Approved'])"/>
How about...
<xsl:variable name="totalTc" select="count(
//testcase[
ident!='text' and
ident!='obsolete' and
ident!='rtm' and
ident!='status overview' and
ident!='statistics' and
extendedinfo[
#type='test case status' and
.='Approved']
])"/>
Options
Note in XSLT 2.0, you could instead use:
<xsl:variable name="totalTc" select="count(
//testcase[
not (ident in ('text','obsolete','rtm','status overview','statistics')) and
extendedinfo[#type='test case status' and .='Approved']
])"/>
Also, instead of an expression of the form ...
testcase[ A and B]
... one could instead write....
testcase[A][B]
The former case may be ever so slightly more efficient, but I think personal style should also factor in, in relation to your choice of expression. So for example, one could also put...
<xsl:variable name="totalTc" select="count(//testcase
[not (ident in ('text','obsolete','rtm','status overview','statistics'))]
[extendedinfo[#type='test case status'][.='Approved']])"/>
Use:
count(
//testcase
[extendedinfo[#type='test case status']='Approved'
and
not(teststep
[contains('|text|obsolete|rtm|status overview|statistics',
concat('|',#ident,'|')
)
]
)
]
)