An URI relativization in XQuery
How to relativize an URI to another URI?
uri1
file:/folder1/file2.txt
uri2
file:/folder1/folder2/file1.txt
needed result
开发者_如何学编程relativize-method($uri1, $uri2) == '../file2.txt'
Something like this (will rewrite it in XQuery):
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="my:my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:sequence select="my:RelativeUrl(url[1], url[2])"/>
</xsl:template>
<xsl:function name="my:RelativeUrl" as="xs:string">
<xsl:param name="pUrl" as="xs:string"/>
<xsl:param name="pBase" as="xs:string"/>
<xsl:variable name="vurlSegments" select="tokenize($pUrl, '/')"/>
<xsl:variable name="vbaseSegments" select="tokenize($pBase, '/')"/>
<xsl:variable name="vCommonPrefixLength" select=
"(for $i in 1 to count($vbaseSegments)
return
if($vbaseSegments[$i] ne $vurlSegments[$i])
then $i -1
else ()
)[1]
"/>
<xsl:variable name="vUpSteps" select=
"count($vbaseSegments) -$vCommonPrefixLength "/>
<xsl:sequence select=
"string-join
(
(
(for $i in 1 to $vUpSteps
return
'..'
),
(for $k in 1 to count($vurlSegments) - $vCommonPrefixLength
return
$vurlSegments[$vCommonPrefixLength + $k]
)
),
'/'
)
"/>
</xsl:function>
</xsl:stylesheet>
when applied on this XML document:
<t>
<url>file:/folder1/file2.txt</url>
<url>file:/folder1/folder2/file1.txt</url>
</t>
the wanted, correct result is produced:
../../file2.txt
Update:
Below is a pure XPath 3.0 solution:
let $pUrl := "file:/folder1/file2.txt",
$pBase := "file:/folder1/folder2/folder3/file1.txt",
$urlSegments := tokenize($pUrl, '/'),
$baseSegments := tokenize($pBase, '/'),
$idiff := (for $ind in 1 to max((count($urlSegments), count($baseSegments)))
return $ind[$urlSegments[$ind] ne $baseSegments[$ind]]
) [1]
return
string-join(
((1 to count($baseSegments) - count($urlSegments)) ! '..',
$urlSegments[position() ge $idiff])
, '/')
You could tokenize to get the directories, and then use a recursive function to compute the desired result. Something like the following (tested on try.zorba-xquery.com):
declare function local:compute-relative-uri($absolute as xs:string,
$current as xs:string)
{
local:compute-relative-uri-aux(tokenize($absolute, "/"),
tokenize($current, "/"))
};
declare function local:compute-relative-uri-aux($absolute as xs:string*,
$current as xs:string*)
{
if (head($absolute) eq head($current))
then
local:compute-relative-uri-aux(tail($absolute), tail($current))
else
let $steps := (for $dir in 1 to count($current) - 1 return "..", $absolute)
return string-join($steps, "/")
};
let $absolute := "file:/folder1/file2.txt"
let $current := "file:/folder1/folder2/file1.txt"
return
local:compute-relative-uri($absolute, $current)
精彩评论