Fork me on GitHub
#clojure
<
2023-04-04
>
p-himik06:04:53

Just started using zippers with XML. There's no way for a navigation to preserve grouping, is there? As a concrete example, suppose I have:

<item><type>a</type><value>b</value></item>
<item><type>x</type><value>y</value></item>
I can easily extract all types, all values, or both by using (zip.xml/xml-> item-zipper :type zip/node) or some variation of it. In the end, I need to have a collection of maps, like [{:type "a" :value "b"} {:type "x" :type "y"}]. Seems like I have to extract items as nodes, and then map over them with a function that converts its argument to a zipper and extracts the right text values. Is there no simpler approach?

phronmophobic06:04:33

I've never used zip.xml. What I usually do is use https://grishaev.me/en/zippo/ or something similar. Basically, I would use zippo/loc-find-all to locate all the items and then use zippo/loc-find on each item to find the type and value. I'm not sure how much you can assume about the xml you're consuming, but I've also had success mixing and matching zippers with meander.

p-himik06:04:13

The structure is fixed so I can assume everything. In this context, sounds like zippo won't actually bring anything new to the table. I briefly thought about using Specter for it, but decided against it as trying to remember and make sense of its grammar doesn't make me too enthusiastic.

phronmophobic07:04:06

It seems like you can just reduce over the children of the item element. Each child tag is just a key value pair that you can dump into a map.

phronmophobic07:04:01

Yea, zippo is more useful when you can't make as many assumptions about data. It's great for html parsing where you want to find the title and href for each container element somewhere inside the html.

phronmophobic07:04:06

(->> zitem
     (z/children)
     (into {}
           (map (fn [node]
                  [(:tag node) (-> node :content first)]))))

p-himik07:04:52

I should've added - the items have more nodes than I need. :) So I'll end up filtering as well, and it'll end up being more code than with a zipper->nodes->zippers->nodes chain.

p-himik07:04:50

The question is more about zippers themselves rather than about how to do it in general.

phronmophobic07:04:39

in that case, zippo/loc-find or zippo/loc-find-all should be helpful for finding the relevant children of each item element.

p-himik07:04:34

But that's what that :type already does in zip.xml/xml->. But I just discovered that I don't need to call zip/node to get an item because instead I should be iterating over already existing zippers, the ones that (zip.xml/xml-> ... :item) returns. So the chain becomes one link shorter.

cddr07:04:44

I didn't really grok zippers until I read this article by my former team mate https://grishaev.me/en/clojure-zippers/ He makes the point in that article that zippers sweet spot is when you're trying to process docs where the structure is unknown ahead of time

đź‘Ť 4
phill10:04:31

However, zippers shine in the case where you must modify the tree. For the given goal, you could use xml-seq! Filter by #(= :item (:type %)). Then map to transform each <item> to a record, again using xml-seq unless some of them have child elements.

respatialized13:04:29

https://github.com/mudge/riveted I think Riveted might be your best bet rather than zippers for this problem. You can search for each of your <item> elements, and it returns a lazy sequence of navigator objects that can then be transformed into your desired format. I used it recently to solve a similar-ish problem and it was both extremely easy to use and highly performant - even my extremely unoptimized code was able to process a lot of complex XML very quickly

đź‘Ť 2
respatialized13:04:52

zippers are really good for some problems, but I personally have trouble keeping track of "where" I am in the tree when thinking about, writing and testing them, which is particularly burdensome if the XML you're working with has a complex internal structure. the XPath navigation in riveted makes the simple stuff simple while still enabling the more complex stuff.

Mark Wardle08:04:01

Just to follow up on this, did you read https://blog.korny.info/2014/03/08/xml-for-fun-and-profit.html#zippers - particularly the way he navigates into the parsed XML and then uses zippers on a per node basis? I copied this and it didn’t feel like too much work once I understood.

p-himik17:04:29

@UFTRLDZEW Thanks for pointing me to Riveted! It's 10x faster than zippers in my use-cases (not even mentioning Xalan, which is atrociously slow, at least when it comes to XPaths).

2
jmckitrick12:04:49

Hi all, I’m looking for a clojure/clojurescript crypto solution. I want to encrypt a payload with a public key in the browser, then decrypt it on the server. I found the javascript browser namespace for the Web Crypto API, and I have been using buddy on the server for a while now. But I cannot seem to find an asymmetric scheme that works in both places. Specifically, the Web Crypto API says the RSA-OAEP is the only asymmetric scheme for encryption (vs sign, etc) and even though buddy has a large number of RSA variations, none seem to correspond to RSA-OAEP. Any suggestions? I’m open to options outside of buddy and the web api, but I’d prefer not to import a library just for this functionality.

jmckitrick13:04:36

oh interesting… let me take a look

jmckitrick13:04:18

Ah, that’s in buddy-sign… I wouldn’t have thought to look there