Есть ли модуль Clojure, эквивалентный Python lxml?

Прошу прощения за второй вопрос по той же теме, но я запутался. Есть ли модуль Clojure, который следует за lxml, хотя бы в общих чертах, или документация с практическими рекомендациями о том, как пройтись по XML-файлу с помощью Clojure?

В Python я могу открыть файл XML с помощью модуля lxml; анализировать мой путь через данные; найдите такие теги, как <DeviceID>, <TamperName>, <SecheduledDateTime>, а затем выполните действие на основе значения одного из этих тегов.

В Clojure мне дали отличные ответы о том, как выполнять синтаксический анализ с использованием data.xml, а затем дополнительно сокращать проанализированную в data.xml информацию, извлекая значения тега :content и помещая информацию в древовидную последовательность.

Однако даже в эти результирующие данные встроены другие теги карты, которые, очевидно, не реагируют на функции key и vals.

Я мог бы взять эти данные и использовать поиск по регулярным выражениям, но я чувствую, что упускаю что-то гораздо более простое.

Данные прямо из data.xml/parse (вызов ret-xml-data) выглядят так, используя различные (first parsed-xml) и другие команды в REPL:

[:tag :TamperExport]
[:attrs {}]
:content
#clojure.data.xml.Element{:tag :Header, :attrs {}, :content 
(#clojure.data.xml.Element{:tag :ExportType, :attrs {}, 
:content ("Tamper Export")} 
#clojure.data.xml.Element{:tag :CurrentDateTime, 
:attrs {}, 
:content ("2012-06-26T15:40:22.063")} :attrs {}, 
:content ("{06643D9B-DCD3-459B-86A6-D21B20A03576}")}

Вот код Clojure, который у меня есть:

(defn ret-xml-data
    "Returns a map of the supplied xml file, as parsed by data.xml/parse."
    [xml-fnam]

    (let [input-xml (try
                        (java.io.FileInputStream. xml-fnam)
                        (catch Exception e))]

        (if-not (nil? input-xml)
            (xmld/parse input-xml)
            nil)))

(defn gen-xml-content-tree
    "Returns a tree-seq with :content extracted."

    [parsed-xml]
    (map :content (first (tree-seq :content :content (:content parsed-xml)))))

Я думаю, что, возможно, нашел повторяющийся шаблон для данных, который позволит мне проанализировать это, не создавая солянку:

xml-lib.core=> (first (second cl1))
#clojure.data.xml.Element{:tag :DeviceId, :attrs {}, :content ("80580608")}
xml-lib.core=> (keys (first (second cl1)))
(:tag :attrs :content)
xml-lib.core=> (vals (first (second cl1)))
(:DeviceId {} ("80580608"))

Спасибо, как всегда.

Изменить: добавить еще несколько тестов.

Результирующие данные, если бы я пробежался по структуре дерева-последовательности, используя функцию вроде дозы, теперь, вероятно, можно было бы проанализировать с помощью предпринятых действий.


person octopusgrabbus    schedule 28.06.2012    source источник
comment
Что именно вы подразумеваете под другими встроенными тегами карты?   -  person deterb    schedule 12.07.2012
comment
Я не могу легко увидеть древовидную структуру в текстовом редакторе, поэтому элементы кажутся встроенными друг в друга. По сути, я хочу, чтобы теги, которые дают мне время/дату, endpointid и любую другую встроенную информацию. Я попал туда, где хотел быть, отфильтровав :content. Это дало мне более простую карту.   -  person octopusgrabbus    schedule 12.07.2012
comment
Вы пробовали использовать clojure.pprint/pprint (кажется, я правильно запомнил). Он автоматически сделает отступы все красиво.   -  person deterb    schedule 13.07.2012


Ответы (2)


Во-первых, трудно точно сказать, что вы пытаетесь сделать. Когда вы работаете над проблемой программирования, это помогает и вам, и другим людям, помогающим вам иметь «маленький случай», который вы можете представить и решить, прежде чем приступить к более крупному делу.

Судя по тому, как это звучит, вы пытаетесь извлечь контент из определенных элементов и выполнять действия на основе этого контента.

Я собрал небольшой XML-файл с простым содержимым, чтобы попробовать:

<root>
    <someele>
        <item1>data</item1>
        <deeper>
            <item2>else</item2>
        </deeper>
    </someele>
</root>

Я разработал его так, чтобы, как мне кажется, он отражал некоторые из основных проблем, связанных с рассматриваемой проблемой, в частности, возможность делать что-то на произвольных уровнях вложенности в XML.

Глядя на замечательную Clojure Cheatsheet, я нашел xml-seq и попытался запустить ее на clojure.data.xml/parsed xml. Последовательность прошла через каждый из элементов, а затем их дочерние элементы, что упростило итерацию по XML.

Чтобы выбрать определенные элементы в последовательности и работать с ними, мне нравится использовать циклы for с :when. :when упрощает ввод тела цикла при выполнении определенных условий. Я также использую семантику «набор как функция», которая проверяет, есть ли что-то в наборе.

(for [ele (xml-seq (load-xml))
      :when (#{:item1 :item2} (:tag ele))]
  [(:tag ele) (first (:content ele))])

Это возвращает последовательность ([:item1 "data"] [:item2 "else"]), с которой затем можно легко воздействовать другими способами.

Одна из ключевых особенностей Clojure, о которой следует помнить, заключается в том, что вам, как правило, не требуется какой-либо специальный API для выполнения каких-либо действий — базовый язык позволяет легко делать большую часть, если не все, что вам нужно сделать. Записи (которые, как вы видите, возвращаются) также являются картами, например, поэтому assoc, dissoc и т. д. работают с ними, и это то, как они предназначены для работы.

Если это не поможет вам получить то, что вам нужно, не могли бы вы предоставить небольшой образец вывода и образец результата, который вы хотите?

person deterb    schedule 12.07.2012

Ближайшая библиотека Clojure, которую я могу придумать для lxml после (очень) краткого просмотра, называется Enlive. Он указан как инструмент для создания шаблонов HTML, но я почти уверен, что методы, которые он использует для выбора элементов HTML, также могут быть применены к XML.

person deterb    schedule 12.07.2012