0

我有一个 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=NORMid=NOI),所以我尝试在 Apache Ant 中自动执行此操作。

最好的情况是为一个固定文件myXML.xml

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

whereN是任意的,所以 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.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(不是必须的),因此这对我来说非常具有挑战性;-)。

非常感谢您提供的任何帮助、提示、问题等。

4

2 回答 2

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 部分从命令行运行以下 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))
于 2014-07-28T13:37:47.577 回答
0

我知道,这已经有将近五年的历史了,但是对于以后有类似问题的任何人来说,这是一种仅使用 XQuery 而不使用 Ant 即可解决的方法。

这应该与处理器无关(我在这里使用 BaseX),只要处理器支持 EXPath 文件模块(主要的支持)。可能是collection()函数的行为不同,BaseX 要么读取它在目录中找到的所有 XML 文件(这是我们在这里使用的方法),要么将路径解释为它自己的内部数据库中的路径。

由于 XML 有一个命名空间 ( "http://metrics.sourceforge.net/2003/Metrics-First-Flat"),我们必须在 XPath 表达式中承认这一点。*:Values/*:Value有两种方法可以做到这一点:我们可以在序言中为元素声明一个默认命名空间(我们的方法在这里),或者我们可以在我们的 XPath 表达式 ( )中的每个元素的名称前面添加一个前缀通配符。

由于结果将是一个字符串序列(我们的 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))
  ))
于 2019-05-17T04:59:06.690 回答