I have an image (in the content) with a src attribute
http://myPage/rss.gif
I wan't to change the attribute. The xsl:template match expression doesn't work..
<replace css:content="#content" css:theme=".content" />
<xsl:template match="img/#src[contains(., 'rss.gif')]">
<xsl:attribute name="src">/++theme++myPackage/images/<xsl:value-of select="." /></xsl:attribute>
</xsl:template>
What am I doing wrong?
In your example you select an img-tag containing the string rss.gif in its src-attribute, this string is probably http://myPage/rss.gif.
Then you add an attribute src to the img-tag beeing its value the concatenation of the strings /++theme++myPackage/images/ and the original value http://myPage/rss.gif.
The original value is added by <xsl:value-of select="." /></xsl:attribute>. This selects the whole value of the src-attribute!
Thus you get something like:
<img id="content" src="/++theme++myPackage/images/http://myPage/rss.gif" />
For test purposes you could first add an img-element whith the expected src-attribute to your theme.html file and check if the image file is accessed. Please check also that the value of src in your content file only contains the string that you want to append to /++theme++myPackage/images/.
BTW, in your example you replace the theme element by class: css:theme=".content". Remind that this will replace all elements with the class content. Consider using id or a narrower selector e.g. div.content or better div#someid.content.
Related
I am trying to generate the following structure in an XSLT template.
<ns:e1>
<child1>some value<child1>
<child2>some value<child2>
<child3>some value<child3>
</ns:e1>
or
<ns:e2>
<child1>some value<child1>
<child2>some value<child2>
<child3>some value<child3>
</ns:e2>
or other elements ns:e3 etc (although finite), based on a template parameter (say type). Typically I could use an xls:choose construct. In such a case, I would be duplicating the child elements (whose values are also template parameters).
Is there a way in XSLT to dynamically assume the element name ns:e1 or ns:e2 so that I can put the child elements once in its parent. I could save maintenance effort later if I have change the child elements or values (change once in one place and avoid bugs due to human errors).
Thanks for your help in advance.
Yes, you may use the xsl:element instruction to do that.
Assuming you always want to have <child1>some value<child1><child2>some value<child2><child3>some value<child3> as children for your parent element, you could rewrite your code like so:
<xsl:variable name="elementName">
<!-- compute the element name here ... -->
</xsl:variable>
<!-- Here we create an element having the name computed in variable elementName -->
<xsl:element name="{$elementName}" namespace="http://www.anamespace.com/and/so/on">
<child1>some value<child1>
<child2>some value<child2>
<child3>some value<child3>
</xsl:element>
I need to add the name of each category to the body class. I have searched for a way to dump the variable for the category name inside of the template.master file with no luck. I can get the page name or meta info but not the category name.
So it would be
<body class="mycategory">
My styling on the whole page (even outside of the entity.category.xml.config) will be affected by this class.
Update: So an example structure on the site would be Clothing > Men > Brand 1 > product 1. I want a a class that shows up for the whole clothing section regardless if I am on a a product, sub category, or sub-sub category.
Start with this in your master file:
<body class='body <aspdnsf:XmlPackage runat="server" PackageName="bodyclass" />'>
Then create an xml package named bodyclass.xml.config. In that file add this:
<xsl:if test="/root/Runtime/EntityName and /root/Runtime/EntityID">
<xsl:text> e-</xsl:text>
<xsl:value-of select="/root/Runtime/EntityName" />
<xsl:text>-</xsl:text>
<xsl:value-of select="/root/Runtime/EntityID" />
</xsl:if>
This will create classes like e-category-53 rather than category name, but they will give you the same flexibility. They will also work for other entity types like sections and manufacturers.
Another useful one here is:
<xsl:text> url-</xsl:text>
<xsl:value-of select="translate(aspdnsf:StrReplace(/root/Runtime/PageName, '.aspx', ''), '.', '')" />
This will generate a body class based on the url like 'url-c-88-sename'. This will apply to ALL pages, not just entity pages.
I am working backwards to create an XML based on a client's desired outcome. I have an attribute in my XSL that I want the end result to look like this
<AlternateId idType="ADID">XYZ</AlternateId>
I have it incorrectly as
<xsl:attribute name="idType">ADID<xsl:value-of select="add:XYZ"/></xsl:attribute>
This, of course, groups ADID and XYZ together.
How do I get them separated and looking like what I want them to look like?
The value-of needs to go outside the attribute as you want it to be element content rather than part of the attribute value.
<xsl:element name="AlternateId">
<xsl:attribute name="idType">ADID</xsl:attribute>
<xsl:value-of select="add:XYZ"/>
</xsl:element>
But if the attribute name is a fixed string you might as well use a literal result element instead:
<AlternateId idType="ADID">
<xsl:value-of select="add:XYZ"/>
</AlternateId>
I am trying to change the hrefs of certain <a> elements in the theme by computing the URLs using values I select from the content. However, I can't figure out how to change the href attribute at all. It seems like the attributes attribute is not understood in the <replace> rule.
Ultimately, I would like to be able to do something like:
<replace css:theme="a.languageswitcher" attributes="href">
<!-- use some XSL logic here to stitch together the new href -->
</replace>
So the following rules work, but are useless to me:
<copy attributes="href" css:theme="a.languageswitcher" css:content="#portal-logo" />
<merge attributes="href" css:theme="a.languageswitcher" css:content="#portal-logo" />
But this one already does not work, the attributes="href" makes it so this rule is ignored.
<replace attributes="href" css:theme="a.languageswitcher" css:content="#portal-logo" />
On the other hand, if I try to rebuild the <a> element from scratch, then I run into the error described by #ross-patterson in his question: Diazo - Conditionally add a class to a theme element:
<replace theme="//a[#class='languageswitcher']">
<a class="languageswitcher">
<xsl:attribute name='href'>
foo
</xsl:attribute>
</a>
</replace>
produces the error:
XSLTApplyError: xsl:attribute: Cannot add attributes to an element if children have been already added to the element.
How can this be done?
Something like this ought to work:
<replace css:theme="a.languageswitcher">
<xsl:for-each css:select="a.languageswitcher">
<xsl:variable name="link">
http://example.com
</xsl:variable>
<xsl:copy-of select="." />
</xsl:for-each>
</replace>
in a theme I used this rule
<replace css:content-children="#hptoolbar-cerca"><xsl:attribute name="href">
<xsl:value-of select="//li[#id='portaltab-catalog']//a/#href" /></xsl:attribute><xsl:apply-templates select="node()"/></replace>
to modify an href of #hptoolbar-cerca with the href from another element in the content.
Maybe this could be useful for you.
Vito
My case is similar. I am happy with my theme code, only want to "replace" its href values. With XPath copied in the Chrome inspector, the simplest rule looks like this:
<copy attributes="href"
theme="/html/body/div[2]/div[1]/div/div[1]/a"
content="//*[#id='portal-logo']" />
I expect to successfully apply this trick to all the href values, but some do not work. I find these <a> elements are in the code block manipulated by JavaScript for implementing Slide effect. I remove all its related JavaScript codes, and copy its "real" XPath again, finally everything works fine as expected. Hope this helpful.
I'm looking to use a variable as part of an XPath expression.
My problem might be the msxsl node-set function... not sure. But don't let that cloud your judgement... read on...
I'm using a .NET function to load up the content file, which is passed in via bespoke XML content. The #file results in an XML file.
The bespoke XML the sits on the page looks like :
<control name="import" file="information.xml" node="r:container/r:group[#id='set01']/r:item[1]" />
The XSL looks like :
<xsl:variable name="document">
<xsl:copy-of select="ext:getIncludedContent(#file)" />
</xsl:variable>
I'm then translating this to a node-set so I can query the document
<xsl:variable name="preset-xml" select="msxsl:node-set($document)" />
The source file I am loading in looks like :
<container>
<group id="set01">
<item>value01</item>
<item>value02</item>
<item>value03</item>
</group>
<group id="set02">
<item>value04</item>
<item>value05</item>
</group>
</container>
It works up until this point. I can see the source file being brought thru and output as XML.
My problem comes in when I am trying to query the source file with an XPath expression fed in from the content.
I've tried :
<xsl:value-of select="$preset-xml/#node" />
Clearly that doesn't work as it looks for #node as a direct child of the loaded in XML.
I've tried :
<xsl:variable name="node" select="#node" />
<xsl:value-of select="$preset-xml/$node" />
But it doesn't like the second variable.
I've tried concat($preset-xml,'/',$node), but that still draws out the entire document in the result.
The only way I can get this working at the moment is to write out the full expression in the template :
<xsl:value-of select="$preset-xml/r:container/r:group[#id='set01']/r:item[1]" />
Which correctly brings thru value01
But that is then a hard coded solution.
I want to develop a solution which can be manipulated from the content to suit any sort of imported content, with the parameters declared in the content file.
p.s. I'm using XSLT 1.0 because the tech admin won't change the Microsoft parser to support later versions of XSL, so any solution would need to be written with that in mind.
There is no feature similar to reflection in XSLT. MS XSLT engine, in particular, compiles XPath queries when the XSLT was loaded, not when it was applied to your XML document. There is a reason to this: it separates the code (XSLT) from the data (input XML).
What are you trying to achieve by mixing up code and data; are you sure you really need to execute an arbitrary XPath expression from the input XML? Think, for example, what if the user will pass in the #name equal to e.g. //*, and then you'll send to the output the content of the entire input XML document, which might contain some sensitive data.
If you really need to do the thing you described, you may write your own XSLT extension object, which will accept the document root and the XPath query and will return the latter applied to the former. Then you'll be able to call it like that:
<xsl:value-of select="myExtensionNs:apply-xpath($preset-xml, #node)"/>
As for your original attempt with concat($preset-xml,'/',$node), concat is the string concatenating function. You supply some arguments to it, and it returns the concatenation of its string representations. So, concat($preset-xml,'/',$node) will return the string value of $preset-xml, concatenated with /, concatenated with the string value of $node. If you were trying to write a needed XPath query, concat('$preset-xml','/',$node) would seem more logical (note the quotes); still, there is no way to evaluate the resulted string (e.g. $preset-xml/r:container/r:group[#id='set01']/r:item[1]) to the value of corresponging XPath query; it could only be sent to output.
One possible trick is to call an parametrized xsl:template which carries out the crucial select comparison part and evaluate it's output as string.
I had a similar problem (count all nodes of an arbitrary attribute value) which I solved this way (btw: $sd = 'document($filename)' so I load a secondary xml files content):
<xsl:template match="/">
<xsl:variable name="string_cntsuccess">
<xsl:for-each select="./Testcase">
<xsl:variable name="tcname" select="#location"/>
<xsl:for-each select="$sd//TestCase[#state='Passed']">
<xsl:call-template name="producedots">
<xsl:with-param name="name" select="$tcname"/>
</xsl:call-template>
</xsl:for-each>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="cntsuccess" select="string-length($string_cntsuccess)"/>
</xsl:template>
<xsl:template name="producedots">
<xsl:param name="name"/>
<xsl:variable name="ename" select="#name"/>
<xsl:if test="$name = $ename">
<xsl:text>.</xsl:text>
</xsl:if>
</xsl:template>