我有一个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
)并按照上面提到的p_i's
创建csv文件}。此外,我想我应该将下面的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的新手,我在Windows下使用BaseX(不是必须的),因此这对我来说非常具有挑战性; - )。
非常感谢任何帮助,提示,问题等。
答案 0 :(得分:0)
感谢您的帮助。我弄清楚了:
可以使用http://ant-contrib.sourceforge.net/tasks/tasks/for.html完成for循环。我对所有源文件进行了迭代(它们的名称存储在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-part从命令行运行以下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))
答案 1 :(得分:0)
我知道,这已经有将近五年的历史了,但是对于以后想到类似问题的任何人来说,这是一种仅使用不带Ant的XQuery即可解决的方法。
这应该与处理器无关(只要我支持Baseath,只要处理器支持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))
))