How do you use `sxpath` to query sxml that has a default namespace?

I am trying to use sxpath to query sxml that has a default namespace. For example, the atom feed for the Racket YouTube channel.

Unfortunately, the xml has a default namespace (last row).

<feed
    xmlns:yt="http://www.youtube.com/xml/schemas/2015" 
    xmlns:media="http://search.yahoo.com/mrss/"
    xmlns="http://www.w3.org/2005/Atom">

...which ssax:xml->sxml applies to produce nodes like this:

'(*TOP*
  (*PI* xml "version=\"1.0\" encoding=\"UTF-8\"")
  (http://www.w3.org/2005/Atom:feed
...
  (http://www.w3.org/2005/Atom:author
    (http://www.w3.org/2005/Atom:name "Racket")
    (http://www.w3.org/2005/Atom:uri
      "https://www.youtube.com/channel/UC8uSLYDanXDnP9Yn8UrTNzQ"))

Suppose that I am trying to retrieve the author's name. Without the default namespace, the xpath would be "/feed/author/name". However, given the namespace, it is more cumbersome

In order to use sxpath I need to provide the namespaces. With the named namespaces (yt and media) this is easy. However, how do I provide the default namespace?

((sxpath "/feed/author/name" '((? . <default-url>))) sxml-string)

Also, thank you to everyone who contributed to the sxml library. I prefer working with it over the standard xml library, particularly because it doesn't drag in all of the whitespace pcdata from the formatting indentations and because sxpath allows for the use of xpath, which is much more concise than se-path.

I've found default namespaces don't play well with XPath tooling in any language or library. SXML is no different. For Racket, one approach I've found that works is to assign a name to the default namespace's URL when parsing the document, and then use it in queries:

#lang racket/base

(require sxml sxml/sxpath)

(define document
  (ssax:xml->sxml
   (open-input-string #<<XML
<feed
    xmlns:yt="http://www.youtube.com/xml/schemas/2015" 
    xmlns:media="http://search.yahoo.com/mrss/"
    xmlns="http://www.w3.org/2005/Atom">
<author>
<name>John Doe</name>
</author>
</feed>
XML
                      )
   '((default . "http://www.w3.org/2005/Atom"))))

(println ((sxpath '(default:feed default:author default:name)) document)) ; '((default:name "John Doe"))

If you're working with stringified XPath expressions, you seem to have to include explicit mappings of namespaces as they appear in the expression to names used in the document:

(println ((sxpath "/default:feed/default:author/default:name" '((default . "default"))) document)) 
;; but this raises an error:
(println ((sxpath "/default:feed/default:author/default:name") document))

(I don't think it's possible to map the default namespace to a name in sxpath)

2 Likes

Your solution is exactly what I needed! I appreciate your time and help so much.