XSLT understand about the generate-id working way - xslt

Further to this link xslt return selection of following siblings
I Just to want to know, here, what is purpose of this generate-id and how it is making sense in this matching protocol.
<xsl:for-each select="/items/item[#lcn='005417714']">
<xsl:for-each select="following-sibling::*[generate-id(preceding-sibling::item[#lcn != ''][1]) = generate-id(current())]">
</xsl:for-each>
</xsl:for-each>
Any idea, pls share.

The specification of generate-id is that it will always return the same ID for the same node and different IDs for different nodes. Thus, comparing the generate-id values of two nodes is the way you check whether they are the same node as opposed to just two nodes that happen to have the same value.
And current() gives you the current "top-level" context node - outside a predicate current() is the same as ., but inside a predicate expression . refers to the node the predicate is testing whereas current() still refers to the "outer .".
Now in your example
following-sibling::*[generate-id(preceding-sibling::item[#lcn != ''][1])
= generate-id(current())]
the outer context node is an item element (the one that the outer for-each is currently looking at). So starting from that item the expression will select:
following-sibling::* --- all the following sibling elements
[ --- such that
generate-id( --- the identity of
preceding-sibling::item[#lcn != ''][1] --- that element's nearest preceding
item with a non-empty lcn attribute
)
= --- is the same as
generate-id(current()) --- the identity of the item we started
with
]
in other words, all the sibling elements between this <item lcn="005417714"> (exclusive) and the next <item lcn="anything-non-empty"> (inclusive).
Taking an example from the linked question:
<item id="00100687" label="A/161i r" lcn="005417714" notes="A/161-182"/>
<item id="00100688" label="A/161i v" lcn="" notes=""/>
<item id="00100819" label="A/182ii v" lcn="" notes=""/>
<item id="00100820" label="A/182iii r" lcn="" notes=""/>
<item id="00100821" label="A/182iii v" lcn="" notes=""/>
<item id="00100822" label="A/183i r" lcn="005417715" notes="A/183-218"/>
<item id="00100823" label="A/183i v" lcn="" notes=""/>
<item id="00100975" label="A/216iii r" lcn="" notes=""/>
if the current context node is the item with id="00100687" then that select would pull out items 00100688, 00100819, 00100820, 00100821 and 00100822. If you wanted to exclude 00100822 you'd have to add another predicate
following-sibling::*[#lcn = '']
[generate-id(preceding-sibling::item[#lcn != ''][1])
= generate-id(current())]

Related

boost property tree adds empty line with write_xml()

I am using boost (version 1.70.0) property tree. If I have this XML (no empty lines):
<Root>
<SomeOtherElement>..</SomeOtherElement>
<Collection>
<Item Attr1=".." attr2="" />
<Item Attr1=".." attr2="" />
</Collection>
</Root>
and I extract a node, insert to another (empty) tree:
auto node = pt.get_child("Root.Collection");
ptree new_pt{};
new_pt.put_child("Collection", node);
std::ostringstream os;
write_xml(os, new_pt);
auto xml = os.str();
I will get the output with empty lines, something like this:
<Collection>
<Item Attr1=".." attr2="" />
<Item Attr1=".." attr2="" />
</Collection>
I have tried different things. I can fix it by iterating over Item elements and adding one by one. Then it works, no extra lines. However, if Item element itself has a child element(s), then again, it will add a bunch of empty lines.
I think it's duplicated with this one, or it's just a bug in the property tree.
https://stackoverflow.com/a/6614372/1292791
To read with the trim flag will fix the problem:
pt::read_xml(filename, tree, boost::property_tree::xml_parser::trim_whitespace);
To write with pretty format:
pt::write_xml(os, tree, boost::property_tree::xml_writer_make_settings<std::string>(' ', 1));

Difference between ancestor and ancestor-or-self

I know about ancestor in xpath but what is this ancestor-or-self.
when we have to use ancestor-or-self.Please give me any examples.
The axis name is quite self-explanatory I think. ancestor axis selects only ancestor(s) of current context element, while ancestor-or-self selects both ancestor(s) and the current element itself. Consider the following XML for example :
<root>
<item key="a">
<item key="b" target="true">
<context key="c" target="true"/>
</item>
</item>
</root>
The following xpath which uses ancestor axis, will find the item b because it has target attribute equals true and b is ancestor of context element. But the XPath won't select context element itself, despite it has target equals true :
//context/ancestor::*[#target='true']
output of the above XPath in xpath tester :
Element='<item key="b" target="true">
<context key="c" target="true" />
</item>'
contrasts with ancestor-or-self axis which will return the same, plus context element :
//context/ancestor-or-self::*[#target='true']
output of the 2nd XPath :
Element='<item key="b" target="true">
<context key="c" target="true" />
</item>'
Element='<context key="c" target="true" />'
Ancestor and Ancestor-or-self are the XPath Axes. An axis is a node-set relative to the current node.
The ancestor axis selects all the ancestors, i.e. parent, grandparent, etc of the current node whereas the ancestor-or-self selects all the ancestors, i.e. parent, grandparent, etc. of the current node and the current node itself.
Ancestor-or-self is generally used to locate the XML document tags or in XSLT that is a transformation language for XML designed to transform structured documents into other formats (such as XML, HTML, and plain text).
I believe that you may not need these axes to find the XPath in Selenium Webdriver as it deal with HTML tags not the XML tags and there are many other XPath axes that may help in finding the elements.

Xpath to sum duplicate elements

I have requirement on to find the duplicate elements in the input xml and sum the quantity with single record as output.
Input xml is:
<Input>
<A1>
<NAME>A</NAME>
<QTY>1</QTY>
</A1>
<A1>
<NAME>A</NAME>
<QTY>2</QTY>
</A1>
<A2>
<NAME>B</NAME>
<QTY>3</QTY>
</A2>
<A1>
<NAME>A</NAME>
<QTY>5</QTY>
</A1>
<A2>
<NAME>b</NAME>
<QTY>8</QTY>
</A2>
</Input>
output should be as below:
<Input>
<A1>
<NAME>A</NAME>
<QTY>8</QTY>
</A1>
<A2>
<NAME>B</NAME>
<QTY>11</QTY>
</A2>
</Input>
If you want to sum several nodes of type number you can use the XPath sum() function. This adds all your QTY nodes:
sum(//QTY)
If you just want to add the nodes that are below A1 you can use:
sum(/Input/A1/QTY)
or
sum(//A1/QTY)
which will have the same result considering the source you provided.
You can select the first A1 with the same name using
//A1[1]
So, to obtain the result you want you could match A1[1] in a template and call sum(//A1/QTY) or sum(/Input/A1/QTY) inside it to obtain the sum. Then you repeat the process with A2.
You can achieve this with two recursive templates:
The sum expression here obtains the value of the node * which may be A1 or A2. The XPath expression compares its name name(current()) with the name() of each child of Input (/Input/*), which will match either A1 or A2, adding the amount in the QTY of each node.

XSLT 2.0 - Two steps sorting

my XML tag contains many items that should be threated as three different groups, with similar (but different) sorting rules.
This is what I want to get:
<items>
<!-- Header - for-each sorting -->
<item name="something1_A"/>
<item name="something2_B"/>
<item name="something3_C"/>
<!-- Body - for-each-group sorting -->
<item name="something4_D"/>
<item name="something4_E"/>
<item name="something5_D"/>
<item name="something5_E"/>
<!-- Footer - for-each sorting -->
<item name="something6_F"/>
<item name="something6_G"/>
<item name="something6_H"/>
</items>
Initially, items order is random.
The first sort should create those three different parts: put everything that is header on the top, everything that is footer on the bottom, and keep everything else where it is. I can determine if something should go in the header, in the body or in the footer looking at its ending (the value after the last underscore).
The second sort should work differently on each of those parts (per-element sorting for header and footer, per-group sorting for body).
I know how I can sort the header, the body and the footer (thanks to this answer), but not how to move them and sort them with different algorithms.
Assuming you have a template that matches items then it's just a case of separating the item elements into three groups, which you say you can do via the endings:
<xsl:variable name="headerItems" select="item[
some $suf in ('_A', '_B', '_C') satisfies ends-with(#name, $suf)]" />
<xsl:variable name="footerItems" select="item[
some $suf in ('_F', '_G', '_H') satisfies ends-with(#name, $suf)]" />
<xsl:variable name="bodyItems"
select="item except ($headerItems | $footerItems)" />
and then handling the three groups in sequence however you need to.

How to find ancestor-or-self that is a child of an element with particular attribute?

I'm working with a very generic XML structure, where everything is an item (well everything relevant to this question anyway).
Based on knowing the item element I'm currently on and the item element that is the parent of the node I'm looking for, I need to find an item. I have a working xpath, but it's fairly resource intensive and I'm looking for something more elegant and cheaper.
The item key=a node is the parent of the element I'm looking for (though it's not actually a child of the document root)
XML:
<root>
<item key="a">
<item key="b">
<item key="c">
<item key="d"/>
</item>
</item>
<item key="e">
<item key="f">
<item key="g"/>
</item>
</item>
</item>
</root>
The actual XML is much deeper and with far more branching.
So for instance, if I'm on the item with key=g, e or f I need to return the item with key=e. If I'm on the item with key b,c or d I need to return the item with key=b.
I'm using this xpath, which is working, but going up and then back down the ancestor-descendant axis seems a far longer trip than I need.
current()
/ancestor-or-self::item[#key='a']
/item[descendant-or-self::* = current()]
Is there a simpler way of doing this, bearing in mind that I only know 1) the node I'm on and 2) the key attribute of the parent of the node I'm looking for?
Just for detail's sake: The XML is Sitecore generated, I'm not actually using the current() function, I'm using the sc_currentitem parameter to set the start node I need to begin processing at.
Thanks in advance.
Use:
ancestor-or-self::*[parent::item[#key='a']]