在Ant中进行迭代,命令行xquery函数调用

时间:2014-07-22 15:35:54

标签: xml csv ant xquery basex

我有一个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.xmlid=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.xmlNORM都是固定的,输出只是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(不是必须的),因此这对我来说非常具有挑战性; - )。

非常感谢任何帮助,提示,问题等。

2 个答案:

答案 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 := "&#10;" (: 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))
  ))