开发者

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)
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜