Get text from node excluding children - xpath

I want to get the text from the node <hello>, excluding its children. I know that /element/hello/text() is supposed to work, but it doesn't.
<element>
<hello><b>Exclude me</b> but not me <b>I'm excluded :(</b> I'm included</hello>
</element>
My output is only:but not me . If I remove the first node, everything works as it is supposed and the second text node (I'm included) is parsed. Here, the native processor behaves similarly. Saxon works as expected. Might this be a bug? I'm using XML filters in LibreOffice 4.4.2.2.

I want to get the text from the node <hello>, excluding its
children. I know that /element/hello/text() is supposed to work, but
it doesn't.
/element/hello/text() does work to select the nodes you want.
However, if you're using xsl:value-of to get the text, it will only return the value from the first node of the selected set. To get all of them, you must do:
<xsl:for-each select="/element/hello/text()">
<xsl:value-of select="." />
</xsl:for-each>
The above applies to XSLT 1.0. In XSLT 2.0,
<xsl:value-of select="/element/hello/text()"/>
will return the text of all selected nodes, separated by a space (unless you specify a different separator).

Related

Need to replace integers with floating points in lines containing a specific string in an xml formatted file

For reasons related to app functionality, we need to massage certain data incoming to a system by replacing an integer value with a fixed length decimal value
Example:
Before
<smile:ordinary code:type="Fields" code:value="25">
After
<smile:ordinary code:type="Fields" code:value="25.000000000">`
I had tried to used a sed command in place to replace with a regex group such as the one below
sed -i 's/\(ordinary.*"[0-9]\+\)/\1.000000000/'
This works fine but there's a file watcher that triggers when the file is modified and if it receives a well formatted file, it ends up adding an extra set of 0s
<smile:ordinary code:type="Fields" code:value="25.000000000.000000000">
I've also struggled to get this working with awk and printf but ideally, i'd replace the integer strictly with a decimal. I've considered using an xsl filter transform as well but I'm not quite as well versed there as with shell commands. I'm open to all suggestions including possibly writing a shell script to loop through each line I guess.
Very easily done in XSLT. It just needs a stylesheet with two rules, the standard identity template that copies elements unchanged by default plus a rule
<xsl:template match="smile:ordinary/#code:value">
<xsl:attribute name="code:value">
<xsl:value-of select="format-number(., '#.000000000')"/>
</xsl:attribute>
</xsl:template>
Plus the required namespace declarations, of course.

XPath expression to select the complete document excluding one element

I have an XML document
<root>
<a>Foo</a>
<b>Bar</b>
<c>Baz</c>
</root>
and need an XPath 1.0 query to obtain the entire document excluding the <b> element, as follows:
<root>
<a>Foo</a>
<c>Baz</c>
</root>
I have tried *[not(self::b)] but this just gives me the original document, as does *[not(ancestor-or-self::b)].
The queries /root/*[not(self::b)] and /root/*[not(ancestor-or-self::b)] work as expected to exclude the element, but omit the parent root element, which we require.
<a>Foo</a>
<c>Baz</c>
Any suggestions on how to achieve this would be gratefully received.
XPath can only select nodes that are there in the input, it cannot modify the input tree in any way. Your input does not contain a root element whose only children are a and c, so you cannot select such an element.
For that you need XSLT or XQuery.

Usage of a variable in an xPath expression

With the definition
<xsl:variable name="testVariable">
<xsl:value-of select="'/author/'"/>
</xsl:variable>
I was hoping that
<xsl:value-of select="concat('./book',$testVariable,'#attribute')" />
returns the same like
<xsl:value-of select="./book/author/#attribute" />
But only the latter returns the actual value of the attribute, the first one just returns the path
./book/author/#attribute
How can I make the first one also return the value of the attribute?
Thanks!
The concat() function returns a string, it doesn't magically interpret that string as the source code of an XPath expression and then evaluate that expression.
Note also that
<xsl:variable name="testVariable">
<xsl:value-of select="'/author/'"/>
</xsl:variable>
can in 99% of cases be rewritten as
<xsl:variable name="testVariable" select="'/author/'"/>
which is not only less code, it's also a lot more efficient. (Sadly the other 1% of cases mean that the optimizer can't do this rewrite automatically.)
Usually you can achieve what you want using
select="/book/*[name()=$testVariable]/#attribute"
Occasionally you need to go a bit beyond that in which case you need something like xsl:evaluate in XSLT 3.0.

Using an XPath on current-group()

I need to select a subset of the nodes of a of the current-group() in an xsl:for-each-group loop. When I use an XPath of the form current-group()/foo, nothing is matched. If, however, I bind the current group to a variable like so:
<xsl:variable name="foo"><xsl:copy-of select="current-group()"/></xsl:variable>
and then use an XPath of the form $foo/foo, I get the expected matches. I suspect that the issue is somehow related with the type of current-group() and how the $foo variable has a different type, but I can't seem to figure it out by myself. Any clues how I can avoid introducing a variable to make the type conversion? Or is it something different?
if you do something like:
<xsl:for-each-group select="foo" group-by="type">
<xsl:value-of select="current-group()[self::foo]"/>
</xsl:for-each-group>
Then current-group() returns sequence of elements
But
<xsl:variable name="foo">
<xsl:copy-of select="current-group()"/>
</xsl:variable>
returns a document node which contains sequence of foo, and then you need to use:
<xsl:value-of select="current-group()/foo"/>

'Select' 2 pieces of info (XSLT file)

I am trying to link our Magento website with Sage 50 with a piece of software.
We would like the customers first name and last name to go into the company field.
Below are the 3 lines I assume I have to tweak:
<Forename><xsl:value-of select="billing_address/firstname"/></Forename>
<Surname><xsl:value-of select="billing_address/lastname"/></Surname>
<Company><xsl:value-of select="billing_address/company"/></Company>
How do I combine first name and last name in 1 line? looking for something like:
<Company><xsl:value-of select="billing_address/firstname, billing_address/lastname"/></Company>
You really need to tell us which version of XSLT you are using. Your proposed code
<xsl:value-of select="billing_address/firstname, billing_address/lastname"/>
is fine in 2.0, and you can get the comma by adding the attribute separator=", "/>. But this won't work in 1.0, where xsl:value-of will only output the first item if you give it a sequence.
First of all, whitespace-only text nodes are ignored by the XSLT engine, so what you tried above can be rewritten like the following:
<Company>
<xsl:value-of select="billing_address/firstname, billing_address/lastname"/>
</Company>
Second, you have to understand that xsl:value-of generates a text node. The following will generate 2 text nodes, with resp. the first and last names:
<Company>
<xsl:value-of select="billing_address/firstname"/>
<xsl:value-of select="billing_address/lastname"/>
</Company>
Then if I understand correctly, you want to seperate both with the string ", ". You can use xsl:text to generate a fixed-content text node:
<Company>
<xsl:value-of select="billing_address/firstname"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="billing_address/lastname"/>
</Company>
In the above, you can put the ", " directly between both value-of, but then you can't control the indentation. Usuaully, when I generate fixed text, I always use xsl:text.
You can give
<Company><xsl:value-of select="concat(billing_address/firstname,', ', billing_address/lastname)"/></Company>
a try...

Resources