Итерация в Ant, вызов функции xquery из командной строки
У меня есть xml-файл - назовите его myXML.xml - вот так:
<?xml version="1.0" encoding="UTF-8"?>
<Metrics info1="1" info2="2" info3="3" xmlns="http://metrics.sourceforge.net/2003/Metrics-First-Flat">
<Metric id = "NORM" description ="Number of Overridden Methods">
<Values per = "type" total = "135" avg = "0.452" stddev = "0.94" max = "5">
<Value name="a" source ="a.java" package ="package.a" value ="1"/>
<Value name="b" source ="b.java" package ="package.b" value ="34"/>
<Value name="c" source ="c.java" package ="package.c" value ="4"/>
<Value name="d" source ="d.java" package ="package.d" value ="99"/>
<Value name="e" source ="e.java" package ="package.e" value ="99"/>
<Value name="f" source ="f.java" package ="package.f" value ="99"/>
<Value name="g" source ="g.java" package ="package.g" value ="99"/>
</Values>
</Metric>
<Metric id = "NOI" description ="Number of Overridden Methods">
<Values per = "type" total = "135" avg = "0.452" stddev = "0.94" max = "5">
<Value name="a" source ="a.java" package ="package.a" value ="10"/>
<Value name="b" source ="b.java" package ="package.b" value ="340"/>
<Value name="c" source ="c.java" package ="package.c" value ="40"/>
<Value name="d" source ="d.java" package ="package.d" value ="990"/>
</Values>
</Metric>
</Metrics>
Потому что я должен оценить десятки таких файлов (например, myXML.xml
) более десятка атрибутов (здесь id=NORM
а также id=NOI
) Я пытался автоматизировать это в Apache Ant.
В лучшем случае было бы получить фиксированный файл (myXML.xml
) CSV-файл в ответ - который будет сохранен как myXML.csv - и выглядит примерно так
NORM 1, 34, 4, 99, 99, 99, 99
NOI 10, 340, 40, 990
Чтобы подойти к этому, я подумал создать файл свойств <property file="metrics.properties"/>
который выглядит как
p_1 = NORM
p_2 = NOI
...
p_N = VG
где N
произвольно, поэтому Ant должен выяснить, N
(в небольшом примере здесь N=2
) и создайте CSV-файл, как указано выше, над всеми p_i's
, Далее, я думаю, мне следует переписать приведенный ниже xquery как функцию файла (myXML.xml
) а также NORM
и запустите его из командной строки. Но я не понимаю, как это сделать.
Следующий xquery частично делает то, что мне интересно:
declare option db:stripns 'true';
for $x in doc("myXML.xml")/Metrics/Metric[@id="NORM"]/Values//Value/@value
return data($x)
но оба myXML.xml
а также NORM
фиксированы и выход просто 1 34 4 99 99 99 99 . Я сохранил этот файл в query.xq
и запустил его в Ant:
<target name="ant" depends="#1">
<echo> ant </echo>
<exec executable="${pathToAnt}/basex.bat" dir="${basedir}" error="${basedir}/output/error.txt">
<arg value = "query.xq"/>
<redirector output="${basedir}/output/myXML.csv" alwayslog="true"/>
</exec>
</target>
Это то, что у меня есть - немного далеко от того, что я собираюсь получить.
Надеюсь, понятно, чего я пытаюсь достичь. Я новичок в xquery, а также в ant, и я использую BaseX (не обязательно) под Windows, поэтому это довольно сложно для меня;-).
Большое спасибо за любую помощь, советы, вопросы и т. Д.
1 ответ
Спасибо за вашу помощь. Я понял:
Цикл for можно выполнить с помощью http://ant-contrib.sourceforge.net/tasks/tasks/for.html. Я сделал итерацию по всем моим исходным файлам (их имена хранятся в fileNames
) который выглядит как
<for list="${fileNames}" delimiter="," param="nameIter">
<sequential>
<echo> loop over fileNames: nameIter=@{nameIter} </echo>
<exec executable="${pathToAnt}/basex.bat" dir="${basedir}" error="${basedir}/output/error_baseX/@{nameIter}Error.txt">
<arg value="-b$importList=${metricsList}" />
<arg value="-b$name=@{nameIter}"/>
<arg value="./source_data/data/query.xq"/>
<redirector output="${basedir}/output/@{nameIter}.csv" alwayslog="true"/>
</exec>
</sequential>
</for>
Теперь exec-часть запускает следующий xquery из командной строки, где переменная metricsList
состоят из всех метрик, которые меня интересуют. Например, в приведенном выше XML-коде это будет metricsList=NORM,NOI
, Файл xquery query.xq
является
declare option db:stripns 'true';
declare variable $name external;
declare variable $importList external;
declare variable $list as xs:string* := tokenize($importList, ',');
for $i in $list
let $x := doc($name)/Metrics/Metric
let $nl := " " (: this is a newline:)
return ($nl,data($x[@id=$i]/Values/../@id), data($x[@id=$i]/Values/Value/@value))
Я вижу, этому уже почти пять лет, но для любого, кто придет позже с похожим вопросом, этот способ может быть решен только с помощью XQuery без Ant.
Это должно зависеть от процессора (здесь я использую BaseX), если процессор поддерживает файловый модуль EXPath (основные из них). Может быть, что collection()
Функция ведет себя по-разному, BaseX либо читает все файлы XML, которые он находит в каталоге (это метод, который мы здесь используем), либо интерпретирует путь как путь в своей собственной внутренней базе данных.
Поскольку XML имеет именованное пространство имен ("http://metrics.sourceforge.net/2003/Metrics-First-Flat"
) мы должны признать это в наших выражениях XPath. Есть два способа сделать это: мы можем объявить пространство имен по умолчанию для элементов в прологе (наш подход здесь) или просто добавить подстановочный знак префикса перед именем каждого элемента в нашем выражении XPath (*:Values/*:Value
).
Поскольку результатом будет последовательность строк (и нам нужна одна строка для нашего CSV), мы объединяем сегменты и добавляем буквальную запятую в конце концов, кроме последнего сегмента, с помощью небольшой встроенной функции, составляем финальную строку с помощью string-join()
и записать CSV на диск.
declare default element namespace "http://metrics.sourceforge.net/2003/Metrics-First-Flat";
let $path := "/path/to/folder/with/XML/files/"
let $docs := collection($path)
let $decorate := function($sequence) {
for $i in subsequence($sequence, 1, count($sequence) - 1)
return $i || ","
,subsequence($sequence, count($sequence))
}
for $doc in $docs/Metrics
count $cnt (: this helps to create sequential file names:)
let $norm := ( "NORM",
for $metric in $doc/Metric[@id="NORM"]
return $metric/Values/Value/@value/data()
)
let $noi := ( "NOI",
for $metric in $doc/Metric[@id="NOI"]
return $metric/Values/Value/@value/data()
)
return
file:write(
concat("/path/to/file-", $cnt, ".csv")
,concat(
string-join($decorate($norm))
,out:nl() (: BaseX specific, creates a 'newline' :)
,string-join($decorate($noi))
))