XSLT - Extracting name-value pairs

E

Ebenezer

Let's suppose I have some nodes in an XML file, with an URL attribute:

<node url="mypage.php?name1=value1&foo=bar&foo2=bar2&name2=value0" />
<node url="myotherpage.php?name4=value4&foo=bar3&foo2=bar5&name2=value8" />

and so on.

Let's suppose I want to retrieve this @url parameter, BUT ONLY with the
values, in querystring, associated with "foo" and "foo2" (thus
discarding name1, name2, name4 and every other different ones).

In other words, I must obtain:

mypage.php?foo=bar&foo2=bar2
myotherpage.php?&foo=bar3&foo2=bar5
.... and so on.

Is there a convenient way, in a transformation with XSL, to obtain this
string manipulation? (I'd prefer to stick to XSLT1.0, if possible)

Thanks in advance for your help.
 
M

Martin Honnen

Ebenezer said:
Let's suppose I have some nodes in an XML file, with an URL attribute:

<node url="mypage.php?name1=value1&foo=bar&foo2=bar2&name2=value0" />
<node url="myotherpage.php?name4=value4&foo=bar3&foo2=bar5&name2=value8" />

and so on.

Let's suppose I want to retrieve this @url parameter, BUT ONLY with the
values, in querystring, associated with "foo" and "foo2" (thus
discarding name1, name2, name4 and every other different ones).

In other words, I must obtain:

mypage.php?foo=bar&foo2=bar2
myotherpage.php?&foo=bar3&foo2=bar5
... and so on.

Is there a convenient way, in a transformation with XSL, to obtain this
string manipulation? (I'd prefer to stick to XSLT1.0, if possible)

Well
substring-before(node/@url, '?')
would give you the file name, the query string would need to be parsed
which needs a recursive template or an extension function in XSLT 1.0.
In XSLT 2.0 you could use the tokenize function and/or xsl:analyze-string:

<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/2008/mf"
exclude-result-prefixes="xsd mf"
version="2.0">

<xsl:function name="mf:get-query" as="xsd:string">
<xsl:param name="qs" as="xsd:string"/>
<xsl:param name="params" as="xsd:string*"/>
<xsl:variable name="filtered-qs" as="xsd:string*">
<xsl:for-each select="tokenize($qs, '&amp;')">
<xsl:analyze-string
select="."
regex="({string-join($params, '|')})=\w*">
<xsl:matching-substring>
<xsl:sequence select="regex-group(0)"/>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:for-each>
</xsl:variable>
<xsl:sequence select="string-join($filtered-qs, '&amp;')"/>
</xsl:function>

<xsl:template match="node">
<xsl:value-of select="concat(substring-before(@url, '?'), '?',
mf:get-query(substring-after(@url, '?'), ('foo', 'foo2')))"/>
</xsl:template>

</xsl:stylesheet>
 
E

Ebenezer

Thanks a lot for sharing and helping, Martin, your information is so
valuable, I'll carefully study your code.



Martin Honnen ha scritto:
 
J

Johannes Koch

Ebenezer said:
Let's suppose I have some nodes in an XML file, with an URL attribute:

<node url="mypage.php?name1=value1&foo=bar&foo2=bar2&name2=value0" />
<node url="myotherpage.php?name4=value4&foo=bar3&foo2=bar5&name2=value8" />

If you have this, it's not XML. Instead of '&' in the url attribute
value, write '&amp;'.
 
D

Dimitre Novatchev

Ebenezer said:
Let's suppose I have some nodes in an XML file, with an URL attribute:

<node url="mypage.php?name1=value1&foo=bar&foo2=bar2&name2=value0" />
<node url="myotherpage.php?name4=value4&foo=bar3&foo2=bar5&name2=value8"
/>

and so on.

Let's suppose I want to retrieve this @url parameter, BUT ONLY with the
values, in querystring, associated with "foo" and "foo2" (thus discarding
name1, name2, name4 and every other different ones).

In other words, I must obtain:

mypage.php?foo=bar&foo2=bar2
myotherpage.php?&foo=bar3&foo2=bar5
... and so on.

Is there a convenient way, in a transformation with XSL, to obtain this
string manipulation? (I'd prefer to stick to XSLT1.0, if possible)

Thanks in advance for your help.

Using FXSL 1.x and its "str-split-to-words" template, such processing is
trivial.

This transformation:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="ext"

<xsl:import href="strSplit-to-Words.xsl"/>

<!-- To be applied upon: testTokenize2.xml -->

<xsl:eek:utput omit-xml-declaration="yes" indent="yes"/>

<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>

<xsl:template match="@url">
<xsl:attribute name="{name()}">
<xsl:variable name="vHdUrl" select=
"substring-before(.,'?')"
/>
<xsl:variable name="vTlUrl" select=
"substring-after(.,'?')"
/>

<xsl:variable name="vFilteredActions">
<xsl:call-template name="filterActions">
<xsl:with-param name="pInput" select="$vTlUrl"/>
<xsl:with-param name="pMustStart" select="'foo'"/>
</xsl:call-template>
</xsl:variable>

<xsl:value-of select=
"concat($vHdUrl,'?',$vFilteredActions)"
/>
</xsl:attribute>
</xsl:template>

<xsl:template name="filterActions">
<xsl:param name="pInput"/>
<xsl:param name="pMustStart" select="'x'"/>

<xsl:variable name="vTokens">
<xsl:call-template name="str-split-to-words">
<xsl:with-param name="pStr" select="$pInput"/>
<xsl:with-param name="pDelimiters" select="'&amp;'"/>
</xsl:call-template>
</xsl:variable>

<xsl:for-each select=
"ext:node-set($vTokens)/word
[starts-with(.,$pMustStart)]"
<xsl:variable name="vactDelim">
<xsl:if test="position() > 1">&amp;</xsl:if>
</xsl:variable>

<xsl:value-of select="concat($vactDelim, .)"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

when applied on this xml document:

<nodes>
<node
url="mypage.php?name1=value1&amp;foo=bar&amp;foo2=bar2&amp;name2=value0" />
<node
url="myotherpage.php?name4=value4&amp;foo=bar3&amp;foo2=bar5&amp;name2=value8"
/>
</nodes>

produces the wanted results:

<nodes>
<node url="mypage.php?foo=bar&amp;foo2=bar2"/>
<node url="myotherpage.php?foo=bar3&amp;foo2=bar5"/>
</nodes>

Hope this helped.

Cheers,
Dimitre Novatchev
 
E

Ebenezer

Johannes Koch ha scritto:
If you have this, it's not XML. Instead of '&' in the url attribute
value, write '&amp;'.

Of course, but the parser I use won't bother on that.
 
D

Dimitre Novatchev

Ebenezer said:
Dimitre Novatchev ha scritto:

Unfortunately, I use another engine.

Hmm... Did I say that a specific "engine" was necessary for the solution?

FXSL 1.x works with *any* XSLT 1.0 processor that supports the
exsl:node-set() extension function.

There are 3 separate versions for MSXML, Xalan and Saxon, although the last
two XSLT 1.0 processors implement exsl:node-set().

For MSXML, there is a way to implement exslt:node-set() (and in any XSLT
processor that supports extension functions written in inline-JavaScript).
See:

http://www.jenitennison.com/blog/node/24

Lastly, FXSL 2.0 is the latest (since the last 4-5 years) version of FXSL
which is written in and for XSLT 2.0. It doesn't use any extension functions
at all.
 
D

Dimitre Novatchev

Ebenezer said:
Johannes Koch ha scritto:

Of course, but the parser I use won't bother on that.

Then this is not a true (compliant) XML parser.
 
D

Dimitre Novatchev

Unfortunately, I use another engine.
Hmm... Did I say that a specific "engine" was necessary for the solution?

Or did you think that FXSL was an "engine"?

It most certainly isn't an "engine".

FXSL is a library of functions/templates, written in pure XSLT. As such, its
functions can be used under any compliant XSLT 2.0 processor or any
compliant XSLT 1.0 processor that either implements the exsl:node-set()
extension function, or is one of the following XSLT 1.0 processors: MSXML
(3,4,6), Xalan, Saxon.

Cheers,
Dimitre Novatchev
 
E

Ebenezer

Dimitre Novatchev ha scritto:
Or did you think that FXSL was an "engine"?
It most certainly isn't an "engine".
FXSL is a library of functions/templates, written in pure XSLT. As such, its
functions can be used under any compliant XSLT 2.0 processor or any
compliant XSLT 1.0 processor that either implements the exsl:node-set()
extension function, or is one of the following XSLT 1.0 processors: MSXML
(3,4,6), Xalan, Saxon.

Yes, actually I thought that it was an engine, never heard of this library.
Lot of thanks for sharing and helping, Dimitre, I'll dig into that too.
Cheers.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,002
Messages
2,570,258
Members
46,857
Latest member
ArleenWill

Latest Threads

Top