Pass a Request.QueryString into a xslt doc - xslt

I have a URL that looks like this:
www.example.com/careers/job.aspx?jobTitle=analytics-developer
And I would like to pass the 'jobTitle' into an xslt file using <%=Request.QueryString["jobTitle"]%>
The code I am using in job.aspx to pass information to the jobTitle.xslt file is this:
<asp:Xml ID="Xml1" runat="server" DocumentSource="~/_/xml/Jobs.xml" TransformSource="~/_/xslt/jobTitle.xslt"></asp:Xml>
Where Jobs.xml is the xml file with all the job info.
Jobs.xml looks sth like this:
<?xml version="1.0" encoding="utf-8" ?>
<jobs>
<devjobs>
<job>
<title>Analytics Developer</title>
<group>Mobile Analytics Group</group>
<url>analytics-developer</url>
<shortdesc>We are looking for talented developers.</shortdesc>
</job>
</devjobs>
</jobs>
Basically I want to be able to write in the xslt file that "if the url element from the Jobs.xml file is equal to the 'jobTitle' given in the url then we show that job's details".
Is this possible?

The asp XML control has a "TransformArgumentList" property which can be used to pass paramters to your XSLT. To be honest, I don't know if you can it directly on the asp:xml tag itself, but you can always set in your code behind, in the page_load event, for example
protected void Page_Load(Object Sender, EventArgs e)
{
string param = Request.QueryString["jobTitle"];
XsltArgumentList args = new XsltArgumentList();
args.AddParam("jobtitle", "", param);
Xml1.TransformArgumentList = ags;
}
Then, in your XSLT file itself, you would need to have a xsl:param element accordingly:
<xsl:param name="jobtitle" />
You can then access this just like any variable (by refering to it as $jobtitle).

Related

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>

not able to remove tags that "xsi:nil" in them via xslt

I have following xml which contains several xml tags with xsi:nil="true". These are tags that are basically null. I am not able to use/find any sxlt transformer to remove these tags from the xml and obtain the rest of the xml.
<?xml version="1.0" encoding="utf-8"?>
<p849:retrieveAllValues xmlns:p849="http://package.de.bc.a">
<retrieveAllValues>
<messages xsi:nil="true" />
<existingValues>
<Values>
<value1> 10.00</value1>
<value2>123456</value2>
<value3>1234</value3>
<value4 xsi:nil="true" />
<value5 />
</Values>
</existingValues>
<otherValues xsi:nil="true" />
<recValues xsi:nil="true" />
</retrieveAllValues>
</p849:retrieveAllValues>
The reason of error you get
[Fatal Error] file2.xml:5:30: The prefix "xsi" for attribute "xsi:nil" associated with an element type "messages" is not bound.
is absence of prefix named "xsi" declared, you should specify it in root element such as:
<p849:retrieveAllValues xmlns:p849="http://package.de.bc.a"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<retrieveAllValues>
<messages xsi:nil="true" />
// other code...
update
If you could not change xml document you're receiving from webservice, you could try next approach(if this approach is acceptable for you):
Change your xslt document to process xml documents without specifying element prefixes
Set property namespaceAware of DocumentBuilderFactory to false
After this yout transformer shouldn't complain
It doesn't look like this is going to be possible in XSLT - because of the missing namespace declarations you have to parse the XML file with a non-namespace-aware parser, but all the XSLT processors I've tried don't get on well with such documents, they must rely on some information that is only present when parsing with namespace awareness enabled, even if the document in question doesn't actually contain any namespaced nodes.
So you'll have to approach it a different way, for example by traversing the DOM tree yourself. Since you say you're working in Java, here's an example using Java DOM APIs (the example runs as-is in the Groovy console, or wrap it up in a proper class definition and add whatever exception handling is required to run it as Java)
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.w3c.dom.ls.*;
public void stripNils(Node n) {
if(n instanceof Element &&
"true".equals(((Element)n).getAttribute("xsi:nil"))) {
// element is xsi:nil - strip it out
n.getParentNode().removeChild(n);
} else {
// we're keeping this node, process its children (if any) recursively
NodeList children = n.getChildNodes();
for(int i = 0; i < children.getLength(); i++) {
stripNils(children.item(i));
}
}
}
// load the document (NB DBF is non-namespace-aware by default)
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document xmlDoc = db.parse(new File("input.xml"));
stripNils(xmlDoc);
// write out the modified document, in this example to stdout
LSSerializer ser =
((DOMImplementationLS)xmlDoc.getImplementation()).createLSSerializer();
LSOutput out =
((DOMImplementationLS)xmlDoc.getImplementation()).createLSOutput();
out.setByteStream(System.out);
ser.write(xmlDoc, out);
On your original example XML this produces the correct result:
<?xml version="1.0" encoding="UTF-8"?>
<p849:retrieveAllValues xmlns:p849="http://package.de.bc.a">
<retrieveAllValues>
<existingValues>
<Values>
<value1> 10.00</value1>
<value2>123456</value2>
<value3>1234</value3>
<value5/>
</Values>
</existingValues>
</retrieveAllValues>
</p849:retrieveAllValues>
The empty lines are not actually empty, they contain the whitespace text nodes either side of the removed elements, as only the elements themselves are being removed here.

XSLT transformation passing parameters

I am trying to pass parameters during an XSLT transformation. Here is the xsl stylesheet.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="param1" select="'defaultval1'" />
<xsl:param name="param2" select="'defaultval2'" />
<xsl:template match="/">
<xslttest>
<tagg param1="{$param1}"><xsl:value-of select="$param2" /></tagg>
</xslttest>
</xsl:template>
</xsl:stylesheet>
The following in the java code.
File xsltFile = new File("template.xsl");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document stylesheet = builder.parse("template.xsl");
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer xsltTransformer = transformerFactory.newTransformer(new DOMSource(stylesheet));
//Transformer xsltTransformer = transformerFactory.newTransformer(new StreamSource(xsltFile));
xsltTransformer.setParameter("param1", "value1");
xsltTransformer.setParameter("param2", "value2");
StreamResult result = new StreamResult(System.out);
xsltTransformer.transform(new DOMSource(builder.newDocument()), result);
I get following errors:-
ERROR: 'Variable or parameter 'param1' is undefined.'
FATAL ERROR: 'Could not compile stylesheet'
However, if i use the following line to create the transformer everything works fine.
Transformer xsltTransformer = transformerFactory.newTransformer(new StreamSource(xsltFile));
Q1. I just wanted to know whats wrong in using a DOMSource in creating a Transformer.
Q2. Is this one of the ideal ways to substitute values for placeholders in an xml document? If my placeholders were in a source xml document is there any (straightforward) way to substitute them using style sheets (and passing parameters)?
Q1: This is a namespace awareness problem. You need to make the DocumentBuilderFactory namespace aware:
factory.setNamespaceAware(true);
Q2: There are several ways to get the values from an external xml file. One way to do this is with the document function and a top level variable in the document:
<!-- Loads a map relative to the template. -->
<xsl:variable name="map" select="document('map.xml')"/>
Then you can select the values out of the map. For instance, if map.xml was defined as:
<?xml version="1.0" encoding="UTF-8"?>
<mappings>
<mapping key="value1">value2</mapping>
</mappings>
You could remove the second parameter from your template, then look up the value using this line:
<tagg param1="{$param1}"><xsl:value-of select="$map/mappings/mapping[#key=$param1]"/></tagg>
Be aware that using relative document URIs will require that the stylesheet has a system id specified, so you will need to update the way you create your DOMSource:
DOMSource source = new DOMSource();
source.setNode(stylesheet);
source.setSystemId(xsltFile.toURL().toString());
In general, I suggest looking at all of the options that are available in Java's XML APIs. Assume that all of the features available are set wrong for what you are trying to do. I also suggest reading the XML Information Set. That specification will give you all of the definitions that the API authors are using.

Query string value as xsl parameter

Is there anyway in which I can get the value of a querystring variable and work with it with xsl. I tried with <xsl:param name="qsVariableName">, but had no success, it doesnt break but it gives me an empty value when I try to input it like this.
www.example.com?qsVariableName=true
<xsl:param name="qsVariableName" />
<xsl:value-of select="$qsVariableName"></xsl:value-of>
Querystring parameters from either the source XML file or the XSLT are not automatically mapped to set <xsl:param> in your stylesheet.
The <xsl:param> need to be explicitly set when the transform is invoked. Depending upon the environment and how your are invoking it there is different syntax for setting the parameters.
In Java, you would set the parameter with something like this:
javax.xml.transform.Transformer trans =
transFact.newTransformer(xsltSource);
trans.setParameter("qsVariableName", "true");
In XSLT 2.0 you could use the document-uri() function to obtain the URL of the source XML file and then parse that value to obtain a sequence of the querystring parameter(s) and value(s).
tokenize(substring-after(document-uri(/), '?'), '&')
For instance, with the code above if you were transforming an XML file with the url: http://example.com/file.xml?qsVariableName=true it would return "qsVariableName=true".

XSLT; parse escaped text to a node-set and extract subelements

I've been fighting with this problem all day and am just about at my wit's end.
I have an XML file in which certain portions of data are stored as escaped text but are themselves well-formed XML. I want to convert the whole hierarchy in this text node to a node-set and extract the data therein. No combination of variables and functions I can think of works.
The way I'd expect it to work would be:
<xsl:variable name="a" select="InnerXML">
<xsl:for-each select="exsl:node-set($a)/*">
'do something
</xsl:for-each>
The input element InnerXML contains text of the form
<root><elementa>text</elementa><elementb><elementc/><elementd>text</elementd></elementb></root>
but that doesn't really matter. I just want to navigate the xml like a normal node-set.
Where am I going wrong?
In case you can use Saxon 9.x, it provides the saxon:parse() extension function exactly for solving this task.
what I've done is had a msxsl script in the xslt ( this is in a windows .NET environment):
<msxsl:script implements-prefix="cs" language="C#" >
<![CDATA[
public XPathNodeIterator parse(String strXML)
{
System.IO.StringReader rdr = new System.IO.StringReader(strXML);
XPathDocument doc = new XPathDocument(rdr);
XPathNavigator nav = doc.CreateNavigator();
XPathExpression expr;
expr = nav.Compile("/");
XPathNodeIterator iterator = nav.Select(expr);
return iterator;
}
]]>
</msxsl:script>
then you can call it like this:
<xsl:variable name="itemHtml" select="cs:parse(EscapedNode)" />
and that variable now contains xml you can iterate through