XPath selector not working during PDF transformation - xslt

I have a DITA bookmap where I am storing image paths:
<bookmap>
<bookmeta>
<data id="productLogo">
<image href="images/_notrans/frontcover/productLogo.svg" />
</data>
<data id="productPhoto" >
<image href="images/_notrans/frontcover/productPhoto.jpg" />
</data>
</bookmeta>
</bookmap>
Then I attempt to grab the href values by data[#id]:
<xsl:variable name="productLogo"><xsl:value-of select="//data[#id='productLogo']/image/#href" /></xsl:variable>
<xsl:variable name="productPhoto"><xsl:value-of select="//data[#id='productPhoto']/image/#href" /></xsl:variable>
(These XPath expressions match the href when I test against my bookmap in Oxygen.)
During transformation I output:
<xsl:message>productPhoto: <xsl:value-of select="$productPhoto"/></xsl:message>
The value-of is always empty.
However, everything works as expected if I replace the id attribute with numbers:
<xsl:variable name="productLogo"><xsl:value-of select="//data[1]/image/#href" /></xsl:variable>
<xsl:variable name="productPhoto"><xsl:value-of select="//data[2]/image/#href" /></xsl:variable>
What am I doing wrong that's preventing using #id="whatever"?

The XSLT is not applied directly over the Bookmap contents, it is applied over an XML document which contains the bookmap with all topic references expanded in it and with some preprocessing applied to it.
If you set the "clean.temp" parameter to "no" you will find in the temporary files folder a file called something like "mapName_MERGED.xml", that is the XML document over which the XSLT is applied and as you will see in it, all IDs have been changed to be unique in the context of the entire XML document.
When usually working with data elements you should set the #name attribute to them like:
<data name="productLogo">
and match that name in the XSLT code.
There are examples of using "data" in the DITA 1.2 specs as well:
http://docs.oasis-open.org/dita/v1.2/os/spec/langref/data.html#data

Another option, depending on your needs, is to develop a naming convention for the product photos and use the element to build the URI. As the product logo shouldn't change for a product family, it wouldn't hurt to hard-code that in the XSLT code.

Related

Can I include a variable inside a src attribute of an XSL-FO block?

We have 60-odd images that we want to include, and want to insert them into a doc using a variable name in the src attribute. Here is the code that currently isn't working:
Without XSL:-
<var name="Request.Data.Communication.AddressStructured.Sender.OrgId" type="string" />
<fo:block xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:fox="http://xmlgraphics.apache.org/fop/extensions" xmlns:svg="http://www.w3.org/2000/svg" xmlns:th="http://www.thunderhead.com/XSL/Extensions" font-family="Frutiger 45 Light">
<fo:external-graphic content-height="30mm" content-width="100mm" src="cms:///Resources/Images/Request.Data.Communication.AddressStructured.Sender.OrgId.jpg" />
</fo:block>
With XSL:-
<xsl:block xmlns:xsl="http://www.w3.org/1999/XSL/Format" xmlns:fox="http://xmlgraphics.apache.org/fop/extensions" xmlns:svg="http://www.w3.org/2000/svg"
<xsl:var name="Request.Data.Communication.AddressStructured.Sender.OrgId" select="Request.Data.Communication.AddressStructured.Sender.OrgId"/>
<fo:block xmlns:fo="http://www.w3.org/1999/XSL/Transform" xmlns:fox="http://xmlgraphics.apache.org/fop/extensions" xmlns:svg="http://www.w3.org/2000/svg" <fo:external-graphic content-height="30mm" content-width="100mm" src="cms:///Resources/Images/${Request.Data.Communication.AddressStructured.Sender.OrgId}.jpg" />
</fo:block>
</xsl:block>
You might want {$Request.Data.Communication.AddressStructured.Sender.OrgId} rather than ${Request.Data.Communication.AddressStructured.Sender.OrgId}, otherwise read on...
Getting from your source XML to PDF output is a two-step process (unless, that is, you author documents directly in the XSL-FO vocabulary). The steps are:
An XSLT transformation transforms your XML into XML in the XSL-FO vocabulary that an XSL Formatter understands
An XSL Formatter formats the XSL-FO to make pages and outputs those pages as PDF, SVG, etc.
This graphic from the XSL 1.1 Recommendation (https://www.w3.org/TR/xsl11/#d0e147) tries to illustrate the process:
The XSLT stage has variables, but the XSL-FO stage does not. (You can write expressions for the value of (most) XSL-FO properties, but the expression language (see https://www.w3.org/TR/xsl11/#d0e5032) doesn't stretch to having variables.)
So, in your XSLT stylesheet, you would have something like:
{$Request.Data.Communication.AddressStructured.Sender.OrgId}.jpg
where:
$Request.Data.Communication.AddressStructured.Sender.OrgId is a variable (or parameter) reference. We don't have enough information to know how you'd define the variable.
{...} is an Attribute Value Template (AVT) that is used when you want to evaluate an expression to generate some or all of an attribute value.
The output from the XSLT stage would include the literal string resulting from evaluating the expression, and the XSL Formatter will use the actual URL to locate the image correctly.

Retrieving sub-element attribute's value based on uniq-key with xslt ant

My xml looks something like this:
<someRoot
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../my.xsd">
<position name="quality">
<db>
<server name="blah-blah" type="xyz"/>
</db>
<some-more-element value="12345"/>
<my-needy-element>
<my-needy-sub-element name="uk1"
value="a,b,c"
more-attr="some-val"/>
<my-needy-sub-element name="uk2"
value="x,b,y,c"
more-attr="some-diff-val"/>
</my-needy-element>
</position>
</someRoot>
Retrieving say my single db-server name works after I do xmlproperty & samy something like : ${db.server(name)}. Now I am trying to retrieve value for sub elements - but it always
gives me all row values matching it. eg: ${my-needy-element.my-needy-sub-element(more-attr)} will print some-val,some-diff-val . How do I get attribute value matching
to paricular unique name , something like {my-needy-element.my-needy-sub-element(more-attr) with name="uk1"}
Adding editing comment: Is there any way even if I use xslt ant-task by passing 'uk1' & get required sub-element attribute value? with xsl:if or key match
Amending more:
Have tried with xsl & then an xslt ant , but it doesnot seem to give what I am looking for:
<xsl:template match="/someRoot/my-needy-element/my-needy-sub-element">
<someRoot>
<xsl:key name="my-needy-sub-element" match="text()" use="#name"/>
<xsl:apply-templates select="key('name',$xslParamPassed)"/>
</someRoot>
</xsl:template>
thanks,
You can't do this with xmlproperty but you might consider the third-party xmltask which lets you use full-fledged XPath expressions:
<xmltask source="file.xml">
<copy path="/someRoot/position/my-needy-element/my-needy-sub-element[#name='uk1']/#more-attr"
property="more.attr" />
</xmltask>
<echo>${more.attr}</echo>

how to add root node tag in a XML document with XSLT

Iam parsing the xml document in SSIS through the xmlsource. It does not have any root tag. So iam trying to add the root tag to my xml document through XSLT, but getting the error as
[XML Task] Error: An error occurred with the following error message: "There are multiple root elements. Line 11, position 2.".
what is the XSL to be used to add the root element.? Please help..this is very urgent..
Please find the xml source below
<organizational_unit>
<box_id>898</box_id>
<hierarchy_id>22</hierarchy_id>
<parent_box_id>0</parent_box_id>
<code>Team</code>
<description />
<name>CAPS Teams</name>
<manager_title />
<level>0</level>
</organizational_unit>
<organizational_unit>
<box_id>967</box_id>
<hierarchy_id>31</hierarchy_id>
<parent_box_id>0</parent_box_id>
<code>main</code>
<description />
<name>Protegent</name>
<manager_title />
<level>0</level>
<organizational_unit>
<box_id>968</box_id>
<hierarchy_id>31</hierarchy_id>
<parent_box_id>967</parent_box_id>
<code>19L</code>
<description>19L</description>
<name>19L</name>
<level>1</level>
<managers>
<manager>
<hierarchy_mgr_id>243</hierarchy_mgr_id>
<hierarchy_id>31</hierarchy_id>
<box_id>968</box_id>
<rep_id>19499</rep_id>
<unique_rep_id>100613948</unique_rep_id>
<first_name>Ed</first_name>
<last_name>Kill</last_name>
</manager>
</managers>
</organizational_unit>
<organizational_unit>
<box_id>1152</box_id>
<hierarchy_id>31</hierarchy_id>
<parent_box_id>967</parent_box_id>
<code>UNKNOWN_m</code>
<description>Unknown Reps</description>
<name>Unknown Reps</name>
<level>1</level>
</organizational_unit>
</organizational_unit>
Well which XSLT processor do you use, how do you use it? I usually don't suggest to use string processing to construct XML but if you have a fragment without a root element then perhaps doing string concatenation "<root>" + fragment + "</root>" is the easiest way to get a well-formed document. XSLT can work with fragments but how you do that depends on the XSLT processor or XML parser you use, for instance .NET can use an XmlReader with XmlReaderSettings with ConformanceLevel set to fragment, which can then be loaded in an XPathDocument (for processing with XSLT 1.0 and XslCompiledTransform) and probably also with Saxon's XdmNode (although I am not sure I remember that correctly).
The stylesheet would then simply do
<xsl:template match="/">
<root>
<xsl:copy-of select="node()"/>
</root>
</xsl:template>
to wrap all top level nodes into a root element.

Translating itunes affiliate rss via xslt

I can't get this working for the life of me. Here is a snippet of the xml I get from an RSS feed from itunes affiliate. I want top print the values within tags but I cannot for some reason:
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns:im="http://itunes.apple.com/rss" xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
<id>http://ax.itunes.apple.com/WebObjects/MZStoreServices.woa/ws/RSS/toppaidapplications/sf=143441/limit=100/genre=6014/xml</id><title>iTunes Store: Top Paid Applications</title><updated>2010-03-24T15:36:42-07:00</updated><link rel="alternate" type="text/html" href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewTop?id=25180&popId=30"/><link rel="self" href="http://ax.itunes.apple.com/WebObjects/MZStoreServices.woa/ws/RSS/toppaidapplications/sf=143441/limit=100/genre=6014/xml"/><icon>http://phobos.apple.com/favicon.ico</icon><author><name>iTunes Store</name><uri>http://www.apple.com/itunes/</uri></author><rights>Copyright 2008 Apple Inc.</rights>
<entry>
<updated>date</updated>
<id>someID</id>
<title>a</title>
<im:name>b</im:name>
</entry>
<entry>
<updated>date2/updated>
<id>someID2</id>
<title>a2</title>
<im:name>b2</im:name>
</entry>
</feed>
If I try <xsl:apply-templates match="entry"/> it spits out the entire contents of file. If I use <xsl:call-template name="entry"> it will show only one entry and I have to use <xsl:value-of select="//*[local-name(.)='name']"/> to get name but that's a hack. I've used xslt before for xml without namespaces and xml that has proper parent child relationships but not like this RSS feed. Notice entry is not wrapped in entries or anything.
Any help is appreciated. I want to use xslt because I want to alter the itunes link to go through my affiliate account - so something automated wouldn't work for me.
You are matching elements that are in no namespace, but the actual elements in the XML document do belong to a (deafult) namspace: xmlns="http://www.w3.org/2005/Atom".
Therefore, you need to declare the namespace in your stylesheet, let's say xmlns:atom="http://www.w3.org/2005/Atom". and then match not just on {elementName} but on {atom:elementName}, where {elementName} in your case is: "entry".

Xslt transform on special characters

I have an XML document that needs to pass text inside an element with an '&' in it.
This is called from .NET to a Web Service and comes over the wire with the correct encoding &
e.g.
T&O
I then need to use XSLT to create a transform but need to query SQL server through a SP without the encoding on the Ampersand e.g T&O would go to the DB.
(Note this all has to be done through XSLT, I do have the choice to use .NET encoding at this point)
Anyone have any idea how to do this from XSLT?
Note my XSLT knowledge isn’t the best to say the least!
Cheers
<xsl:text disable-output-escaping="yes">&<!--&--></xsl:text>
More info at: http://www.w3schools.com/xsl/el_text.asp
If you have the choice to use .NET you can convert between an HTML-encoded and regular string using (this code requires a reference to System.Web):
string htmlEncodedText = System.Web.HttpUtility.HtmlEncode("T&O");
string text = System.Web.HttpUtility.HtmlDecode(htmlEncodedText);
Update
Since you need to do this in plain XSLT you can use xsl:value-of to decode the HTML encoding:
<xsl:variable name="test">
<xsl:value-of select="'T&O'"/>
</xsl:variable>
The variable string($test) will have the value T&O. You can pass this variable as an argument to your extension function then.
Supposing your XML looks like this:
<root>T&O</root>
you can use this XSLT snippet to get the text out of it:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:template match="root"> <!-- Select the root element... -->
<xsl:value-of select="." /> <!-- ...and extract all text from it -->
</xsl:template>
</xsl:stylesheet>
Output (from Saxon 9, that is):
T&O
The point is the <xsl:output/> element. The defauklt would be to output XML, where the ampersand would still be encoded.