{"id":13442373,"url":"https://github.com/clj-commons/hickory","last_synced_at":"2026-01-18T02:28:52.570Z","repository":{"id":3313392,"uuid":"4355933","full_name":"clj-commons/hickory","owner":"clj-commons","description":"HTML as data","archived":false,"fork":false,"pushed_at":"2025-08-26T10:51:17.000Z","size":284,"stargazers_count":676,"open_issues_count":15,"forks_count":55,"subscribers_count":14,"default_branch":"master","last_synced_at":"2026-01-14T07:38:03.258Z","etag":null,"topics":["clojure","html-parser"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/clj-commons.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2012-05-17T07:36:22.000Z","updated_at":"2026-01-10T01:36:20.000Z","dependencies_parsed_at":"2023-01-13T12:25:35.609Z","dependency_job_id":"be39a339-80ae-481f-8023-9b5a87916030","html_url":"https://github.com/clj-commons/hickory","commit_stats":{"total_commits":177,"total_committers":14,"mean_commits":"12.642857142857142","dds":"0.24293785310734461","last_synced_commit":"e7258ca9918622904b04db7e69a6e53d737c3651"},"previous_names":["davidsantiago/hickory"],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/clj-commons/hickory","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clj-commons%2Fhickory","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clj-commons%2Fhickory/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clj-commons%2Fhickory/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clj-commons%2Fhickory/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/clj-commons","download_url":"https://codeload.github.com/clj-commons/hickory/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clj-commons%2Fhickory/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28526571,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T00:39:45.795Z","status":"online","status_checked_at":"2026-01-18T02:00:07.578Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","html-parser"],"created_at":"2024-07-31T03:01:44.974Z","updated_at":"2026-01-18T02:28:52.525Z","avatar_url":"https://github.com/clj-commons.png","language":"Clojure","funding_links":[],"categories":["Clojure"],"sub_categories":[],"readme":"[![Clojars Project](https://img.shields.io/clojars/v/org.clj-commons/hickory.svg)](https://clojars.org/org.clj-commons/hickory)\n[![cljdoc badge](https://cljdoc.org/badge/org.clj-commons/hickory)](https://cljdoc.org/d/org.clj-commons/hickory)\n[![CircleCI](https://circleci.com/gh/clj-commons/hickory.svg?style=svg)](https://circleci.com/gh/clj-commons/hickory)\n\n# Hickory\n\nHickory parses HTML into Clojure data structures, so you can analyze,\ntransform, and output back to HTML. HTML can be parsed into\n[hiccup](http://github.com/weavejester/hiccup) vectors, or into a\nmap-based DOM-like format very similar to that used by clojure.xml. It\ncan be used from both Clojure and Clojurescript.\n\n\n## Usage\n\n### Parsing\n\nTo start, you will want to process your HTML into a parsed\nrepresentation. Once the HTML is in this form, it can be converted to\neither Hiccup or Hickory format for further processing. There are two\nparsing functions, `parse` and `parse-fragment`. Both take a string\ncontaining HTML and return the parser objects representing the\ndocument. (It happens that these parser objects are Jsoup Documents\nand Nodes, but I do not consider this to be an aspect worth preserving\nif a change in parser should become necessary).\n\nThe first function, `parse` expects an entire HTML document, and\nparses it using an HTML5 parser ([Jsoup](http://jsoup.org) on Clojure and\nthe browser's DOM parser in Clojurescript), which will\nfix up the HTML as much as it can into a well-formed document. The\nsecond function, `parse-fragment`, expects some smaller fragment of\nHTML that does not make up a full document, and thus returns a list of\nparsed fragments, each of which must be processed individually into\nHiccup or Hickory format. For example, if `parse-fragment` is given\n\"`\u003cp\u003e\u003cbr\u003e`\" as input, it has no common parent for them, so it must\nsimply give you the list of nodes that it parsed.\n\nThese parsed objects can be turned into either Hiccup vector trees or\nHickory DOM maps using the functions `as-hiccup` or `as-hickory`.\n\nHere's a usage example.\n\n```clojure\nuser=\u003e (use 'hickory.core)\nnil\nuser=\u003e (def parsed-doc (parse \"\u003ca href=\\\"foo\\\"\u003efoo\u003c/a\u003e\"))\n#'user/parsed-doc\nuser=\u003e (as-hiccup parsed-doc)\n([:html {} [:head {}] [:body {} [:a {:href \"foo\"} \"foo\"]]])\nuser=\u003e (as-hickory parsed-doc)\n{:type :document, :content [{:type :element, :attrs nil, :tag :html, :content [{:type :element, :attrs nil, :tag :head, :content nil} {:type :element, :attrs nil, :tag :body, :content [{:type :element, :attrs {:href \"foo\"}, :tag :a, :content [\"foo\"]}]}]}]}\nuser=\u003e (def parsed-frag (parse-fragment \"\u003ca href=\\\"foo\\\"\u003efoo\u003c/a\u003e \u003ca href=\\\"bar\\\"\u003ebar\u003c/a\u003e\"))\n#'user/parsed-frag\nuser=\u003e (as-hiccup parsed-frag)\nIllegalArgumentException No implementation of method: :as-hiccup of protocol: #'hickory.core/HiccupRepresentable found for class: clojure.lang.PersistentVector  clojure.core/-cache-protocol-fn (core_deftype.clj:495)\n\nuser=\u003e (map as-hiccup parsed-frag)\n([:a {:href \"foo\"} \"foo\"] \" \" [:a {:href \"bar\"} \"bar\"])\nuser=\u003e (map as-hickory parsed-frag)\n({:type :element, :attrs {:href \"foo\"}, :tag :a, :content [\"foo\"]} \" \" {:type :element, :attrs {:href \"bar\"}, :tag :a, :content [\"bar\"]})\n```\n\nIn the example above, you can see an HTML document that is parsed once\nand then converted to both Hiccup and Hickory formats. Similarly, a\nfragment is parsed, but it cannot be directly used with `as-hiccup`\n(or `as-hickory`), it must have those functions called on each element\nin the list instead.\n\nThe namespace `hickory.zip` provides\n[zippers](https://clojure.github.io/clojure/clojure.zip-api.html) for\nboth Hiccup and Hickory formatted data, with the functions\n`hiccup-zip` and `hickory-zip`. Using zippers, you can easily traverse\nthe trees in any order you desire, make edits, and get the resulting\ntree back. Here is an example of that.\n\n```clojure\nuser=\u003e (use 'hickory.zip)\nnil\nuser=\u003e (require '[clojure.zip :as zip])\nnil\nuser=\u003e (require '[hickory.render :refer [hickory-to-html]])\nnil\nuser=\u003e (-\u003e (hiccup-zip (as-hiccup (parse \"\u003ca href=foo\u003ebar\u003cbr\u003e\u003c/a\u003e\"))) zip/node)\n([:html {} [:head {}] [:body {} [:a {:href \"foo\"} \"bar\" [:br {}]]]])\nuser=\u003e (-\u003e (hiccup-zip (as-hiccup (parse \"\u003ca href=foo\u003ebar\u003cbr\u003e\u003c/a\u003e\"))) zip/next zip/node)\n[:html {} [:head {}] [:body {} [:a {:href \"foo\"} \"bar\" [:br {}]]]]\nuser=\u003e (-\u003e (hiccup-zip (as-hiccup (parse \"\u003ca href=foo\u003ebar\u003cbr\u003e\u003c/a\u003e\"))) zip/next zip/next zip/node)\n[:head {}]\nuser=\u003e (-\u003e (hiccup-zip (as-hiccup (parse \"\u003ca href=foo\u003ebar\u003cbr\u003e\u003c/a\u003e\")))\n           zip/next zip/next\n           (zip/replace [:head {:id \"a\"}])\n           zip/node)\n[:head {:id \"a\"}]\nuser=\u003e (-\u003e (hiccup-zip (as-hiccup (parse \"\u003ca href=foo\u003ebar\u003cbr\u003e\u003c/a\u003e\")))\n           zip/next zip/next\n           (zip/replace [:head {:id \"a\"}])\n           zip/root)\n([:html {} [:head {:id \"a\"}] [:body {} [:a {:href \"foo\"} \"bar\" [:br {}]]]])\nuser=\u003e (-\u003e (hickory-zip (as-hickory (parse \"\u003ca href=foo\u003ebar\u003cbr\u003e\u003c/a\u003e\")))\n           zip/next zip/next\n           (zip/replace {:type :element :tag :head :attrs {:id \"a\"} :content nil})\n           zip/root)\n{:type :document, :content [{:type :element, :attrs nil, :tag :html, :content [{:content nil, :type :element, :attrs {:id \"a\"}, :tag :head} {:type :element, :attrs nil, :tag :body, :content [{:type :element, :attrs {:href \"foo\"}, :tag :a, :content [\"bar\" {:type :element, :attrs nil, :tag :br, :content nil}]}]}]}]}\nuser=\u003e (hickory-to-html *1)\n\"\u003chtml\u003e\u003chead id=\\\"a\\\"\u003e\u003c/head\u003e\u003cbody\u003e\u003ca href=\\\"foo\\\"\u003ebar\u003cbr\u003e\u003c/a\u003e\u003c/body\u003e\u003c/html\u003e\"\n```\n\nIn this example, we can see a basic document being parsed into Hiccup\nform. Then, using zippers, the HEAD element is navigated to, and then\nreplaced with one that has an id of \"a\". The final tree, including the\nmodification, is also shown using `zip/root`. Then the same\nmodification is made using Hickory forms and zippers. Finally, the\nmodified Hickory version is printed back to HTML using the\n`hickory-to-html` function.\n\n### Selectors\n\nHickory also comes with a set of CSS-style selectors that operate on\nhickory-format data in the `hickory.select` namespace. These selectors\ndo not exactly mirror the selectors in CSS, and are often more\npowerful. There is no version of these selectors for hiccup-format\ndata, at this point.\n\nA selector is simply a function that takes a zipper loc from a hickory\nhtml tree data structure as its only argument. The selector will\nreturn its argument if the selector applies to it, and nil\notherwise. Writing useful selectors can often be involved, so most of\nthe `hickory.select` package is actually made up of selector\ncombinators; functions that return useful selector functions by\nspecializing them to the data given as arguments, or by combining\ntogether multiple selectors. For example, if we wanted to figure out\nthe dates of the next Formula 1 race weekend, we could do something\nlike this:\n\n```clojure\nuser=\u003e (use 'hickory.core)\nnil\nuser=\u003e (require '[hickory.select :as s])\nnil\nuser=\u003e (require '[clj-http.client :as client])\nnil\nuser=\u003e (require '[clojure.string :as string])\nnil\nuser=\u003e (def site-htree (-\u003e (client/get \"http://formula1.com/default.html\") :body parse as-hickory))\n#'user/site-htree\nuser=\u003e (-\u003e (s/select (s/child (s/class \"subCalender\") ; sic\n                              (s/tag :div)\n                              (s/id :raceDates)\n                              s/first-child\n                              (s/tag :b))\n                     site-htree)\n           first :content first string/trim)\n\"10, 11, 12 May 2013\"\n```\n\nIn this example, we get the contents of the homepage and use `select`\nto give us any nodes that satisfy the criteria laid out by the\nselectors. The selector in this example is overly precise in order to\nillustrate more selectors than we need; we could have gotten by just\nselecting the contents of the P and then B tags inside the element\nwith id \"raceDates\".\n\nUsing the selectors allows you to search large HTML documents for nodes of interest with a relatively small amount of code. There are many selectors available in the [`hickory.select`](https://cljdoc.org/d/org.clj-commons/hickory/CURRENT/api/hickory.select) namespace, including:\n\n- `node-type`: Give this function a keyword or string that names the contents of the `:type` field in a hickory node, and it gives you a selector that will select nodes of that type. Example: `(node-type :comment)`\n- `tag`: Give this function a keyword or string that names the contents of the `:tag` field in a hickory node, and it gives you a selector that will select nodes with that tag. Example: `(tag :div)`\n- `attr`: Give this function a keyword or string that names an attribute in the `:attrs` map of a hickory node, and it gives you a selector that will select nodes whose `:attrs` map contains that key. Give a single-argument function as an additional argument, and the resulting selector function will additionally require the value of that key to be such that the function given as the last argument returns true. Example: `(attr :id #(.startsWith % \"foo\"))`\n- `id`: Give this function a keyword or string that names the `:id` attribute in the `:attrs` map and it will return a selector function that selects nodes that have that id (this comparison is case-insensitive). Example: `(id :raceDates)`\n- `class`: Give this function a keyword or string that names a class that the node should have in the `:class` attribute in the `:attrs` map, and it will return a function that selects nodes that have the given class somewhere in their class string. Example: `(class :foo)`\n- `any`: This selector takes no arguments, do not invoke it; returns any node that is an element, similarly to CSS's '*' selector.\n- `element`: This selector is equivalent to the `any` selector; this alternate name can make it clearer when the intention is to exclude non-element nodes from consideration.\n- `root`: This selector takes no arguments and should not be invoked; simply returns the root node (the HTML element).\n- `n-moves-until`: This selector returns a selector function that selects its argument if that argument is some distance from a boundary. The first two arguments, `n` and `c` define the counting: it only selects nodes whose distance can be written in the form `nk+c` for some natural number `k`. The distance and boundary are defined by the number of times the zipper-movement function in the third argument is applied before the boundary function in the last argument is true. See doc string for details.\n- `nth-of-type`: This selector returns a selector function that selects its argument if that argument is the `(nk+c)`'th child of the given tag type of some parent node for some natural `k`. Optionally, instead of the `n` and `c` arguments, the keywords `:odd` and `:even` can be given.\n- `nth-last-of-type`: Just like `nth-of-type` but counts backwards from the last sibling.\n- `nth-child`: This selector returns a selector function that selects its argument if that argument is the `(nk+c)`'th child of its parent node for some natural `k`. Instead of the `n` and `c` arguments, the keywords `:odd` and `:even` can be given.\n- `nth-last-child`: Just like `nth-last-child` but counts backwards from the last sibling.\n- `first-child`: Takes no arguments, do not invoke it; equivalent to `(nth-child 1)`.\n- `last-child`: Takes no arguments, do not invoke it; equivalent to `(nth-last-child 1)`.\n\nThere are also selector combinators, which take as argument some number of other selectors, and return a new selector that combines them into one larger selector. An example of this is the `child` selector in the example above. Here's a list of some selector combinators in the package (see the [API Documentation](https://cljdoc.org/d/org.clj-commons/hickory) for the full list):\n\n- `and`: Takes any number of selectors, and returns a selector that only selects nodes for which all of the argument selectors are true.\n- `or`: Takes any number of selectors, and retrurns a selector that only selects nodes for which at least one of the argument selectors are true.\n- `not`: Takes a single selector as argument and returns a selector that only selects nodes that its argument selector does not.\n- `el-not`: Takes a single selector as argument and returns a selector that only selects element nodes that its argument selector does not.\n- `child`: Takes any number of selectors as arguments and returns a selector that returns true when the zipper location given as the argument is at the end of a chain of direct child relationships specified by the selectors given as arguments.\n- `descendant`: Takes any number of selectors as arguments and returns a selector that returns true when the zipper location given as the argument is at the end of a chain of descendant relationships specified by the selectors given as arguments.\n\nWe can illustrate the selector combinators by continuing the Formula 1 example above. We suspect, to our dismay, that Sebastian Vettel is leading the championship for the fourth year in a row.\n\n```clojure\nuser=\u003e (-\u003e (s/select (s/descendant (s/class \"subModule\")\n                                   (s/class \"standings\")\n                                   (s/and (s/tag :tr)\n                                          s/first-child)\n                                   (s/and (s/tag :td)\n                                          (s/nth-child 2))\n                                   (s/tag :a))\n                     site-htree)\n           first :content first string/trim)\n\"Sebastian Vettel\"\n```\n\nOur fears are confirmed, Sebastian Vettel is well on his way to a fourth consecutive championship. If you were to inspect the page by hand (as of around May 2013, at least), you would see that unlike the `child` selector we used in the example above, the `descendant` selector allows the argument selectors to skip stages in the tree; we've left out some elements in this descendant relationship. The first table row in the driver standings table is selected with the `and`, `tag` and `first-child` selectors, and then the second `td` element is chosen, which is the element that has the driver's name (the first table element has the driver's standing) inside an `A` element. All of this is dependent on the exact layout of the HTML in the site we are examining, of course, but it should give an idea of how you can combine selectors to reach into a specific node of an HTML document very easily.\n\nFinally, it's worth noting that the `select` function itself returns the hickory zipper nodes it finds. This is most useful for analyzing the contents of nodes. However, sometimes you may wish to examine the area around a node once you've found it. For this, you can use the `select-locs` function, which returns a sequence of hickory zipper locs, instead of the nodes themselves. This will allow you to navigate around the document tree using the zipper functions in `clojure.zip`. If you wish to go further and actually modify the document tree using zipper functions, you should not use `select-locs`. The problem is that it returns a bunch of zipper locs, but once you modify one, the others are out of date and do not see the changes (just as with any other persistent data structure in Clojure). Thus, their presence was useless and possibly confusing. Instead, you should use the `select-next-loc` function to walk through the document tree manually, moving through the locs that satisfy the selector function one by one, which will allow you to make modifications as you go. As with modifying any data structure as you traverse it, you must still be careful that your code does not add the thing it is selecting for, or it could get caught in an infinite loop. Finally, for more specialized selection needs, it should be possible to write custom selection functions that use the selectors and zipper functions without too much work. The functions discussed in this paragraph are very short and simple, you can use them as a guide.\n\nThe doc strings for the functions in the [`hickory.select`](https://cljdoc.org/d/org.clj-commons/hickory/CURRENT/api/hickory.select) namespace provide more details on most of these functions.\n\nFor more details, see the [API Documentation](https://cljdoc.org/d/org.clj-commons/hickory/).\n\n## Hickory format\n\nWhy two formats? It's very easy to see in the example above, Hiccup is\nvery convenient to use for writing HTML. It has a compact syntax, with\nCSS-like shortcuts for specifying classes and ids. It also allows\nparts of the vector to be skipped if they are not important.\n\nIt's a little bit harder to process data in Hiccup format. First of\nall, each form has to be checked for the presence of the attribute\nmap, and the traversal adjusted accordingly. Raw Hiccup vectors might\nalso have information about class and id in one of two different\nplaces. Finally, not every piece of an HTML document can be expressed\nin Hiccup without resorting to writing HTML in strings. For example,\nif you want to put a doctype or comment on your document, it has to be\ndone as a string in your Hiccup form containing \"`\u003c!DOCTYPE html\u003e`\" or\n\"`\u003c!--stuff--\u003e`\".\n\nThe Hickory format is another data format intended to allow a\nroundtrip from HTML as text, into a data structure that is easy to\nprocess and modify, and back into equivalent (but not identical, in\ngeneral) HTML. Because it can express all parts of an HTML document in\na parsed form, it is easier to search and modify the structure of the\ndocument.\n\nA Hickory node is either a map or a string. If it is a map, it will\nhave some subset of the following four keys, depending on the `:type`:\n\n- `:type`    - This will be one of `:comment`, `:document`, `:document-type`, `:element`\n- `:tag`     - A node's tag (for example, `:img`). This will only be present for nodes of type `:element`.\n- `:attrs`   - A node's attributes, as a map of keywords to values (for example, {:href \"/a\"}). This will only be present for nodes of type `:element`.\n- `:content` - A node's child nodes, in a vector. Only `:comment`, `:document`, and `:element` nodes have children.\n\nText and CDATA nodes are represented as strings.\n\nThis is almost the exact same structure used by\n[clojure.xml](https://clojure.github.io/clojure/clojure.xml-api.html),\nthe only difference being the addition of the `:type` field. Having\nthis field allows us to process nodes that clojure.xml leaves out of\nthe parsed data, like doctype and comments.\n\n## Obtaining\n\nTo get hickory, add\n\n```clojure\n[org.clj-commons/hickory \"0.7.3\"]\n```\n\nto your project.clj, or an equivalent entry for your Maven-compatible build tool.\n\n## ClojureScript support\n\nHickory works for all web browsers IE9+ (you can find a workaround for IE9 [here](http://stackoverflow.com/questions/9250545/javascript-domparser-access-innerhtml-and-other-properties)).\n\n## Nodejs support\n\nTo parse markup on Nodejs, Hickory requires a Node DOM implementation.\nSeveral are available from [npm](https://www.npmjs.com).\nInstall the npm package or use [lein-npm](https://github.com/RyanMcG/lein-npm).\nHere are some alternatives:\n\n- [jsdom](https://www.npmjs.com/package/jsdom) - **Caution:** this will not work if you're using figwheel\n\n    ```clojure\n\t(set! js/document (.jsdom (cljs.nodejs/require \"jsdom\")))\n\t```\n\n- [xmldom](https://www.npmjs.com/package/xmldom)\n\n    ```clojure\n\t(set! js/DOMParser (.-DOMParser (cljs.nodejs/require \"xmldom\")))\n\t```\n\n## Changes\n\n- Version 0.7.1. Thanks to [Matt Grimm](https://github.com/tkocmathla) for adding the up-pred zipper function.\n\n- Version 0.7.0. Thanks to [Ricardo J. Méndez](https://github.com/ricardojmendez) for the following updates.\n    * Removed dependency on cljx, since it was deprecated in June 2015.\n    * Converted all files and conditionals to cljc.\n    * Moved tests to cljs.test with doo, since cemerick.test was deprecated over a year ago.\n    * Updated Clojure and ClojureScript dependencies to avoid conflicts.\n    * Updated JSoup to 1.9.2, which should bring improved parsing performance.\n\n- Released version 0.6.0.\n    * Updated JSoup to version 1.8.3. This version of JSoup contains bug fixes, but slightly changes the way it\n    handles HTML: some parses and output might have different case than before. HTML is still case-insensitive,\n    of course, but Hickory minor version has been increased just in case. API and semantics are otherwise unchanged.\n\n- Released version 0.5.4.\n    * Fixed project dependencies so ClojureScript is moved to a dev-dependency.\n\n- Released version 0.5.3.\n    * Minor bug fix to accommodate ClojureScript's new type hinting support.\n\n- Released version 0.5.2.\n    * Updates the Clojurescript version to use the latest version of Clojurescript (0.0-1934).\n\n- Released version 0.5.1.\n    * Added `has-child` and `has-descendant` selectors. Be careful with `has-descendant`, as it must do a full subtree search on each node, which is not fast.\n\n- Released version 0.5.0.\n    * Now works in Clojurescript as well, huge thanks to [Julien Eluard](https://github.com/jeluard) for doing the heavy lifting on this.\n    * Reorganized parts of the API into more granular namespaces for better organization.\n    * Added functions to convert between Hiccup and Hickory format; note that this conversion is not always exact or roundtripable, and can cause a full HTML reparse.\n    * Added new selector, `element-child`, which selects element nodes that are the child of another element node.\n    * Numerous bug fixes and improvements.\n\n- Released version 0.4.1, which adds a number of new selectors and selector combinators, including `find-in-text`, `precede-adjacent`, `follow-adjacent`, `precede` and `follow`.\n\n- Released version 0.4.0. Adds the `hickory.select` namespace with many helpful functions for searching through hickory-format HTML documents for specific nodes.\n\n- Released version 0.3.0. Provides a more helpful error message when hickory-to-html has an error. Now requires Clojure 1.4.\n\n- Released version 0.2.3. Fixes a bug where hickory-to-html was not html-escaping the values of tag attributes.\n\n- Released version 0.2.2. Fixes a bug where hickory-to-html was improperly html-escaping the contents of script/style tags.\n\n- Released version 0.2.1. This version fixes bugs:\n    * hickory-to-html now properly escapes text nodes\n    * text nodes will now preserve whitespace correctly\n\n- Released version 0.2.0. This version adds a second parsed data\n  format, explained above. To support this, the API for `parse` and\n  `parse-fragment` has been changed to allow their return values to be\n  passed to functions `as-hiccup` or `as-hickory` to determine the\n  final format. Also added are zippers for both Hiccup and Hickory\n  formats.\n\n## License\n\nCopyright © 2012 David Santiago\n\nDistributed under the Eclipse Public License, the same as Clojure.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclj-commons%2Fhickory","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fclj-commons%2Fhickory","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclj-commons%2Fhickory/lists"}