XMLStarlet: объединение значений узлов от разных родителей на основе позиционного индекса

Я пытаюсь разобрать файлы XML и создать CSV определенных значений. Вот пример XML-файла: http://forecast.weather.gov/MapClick.php?lat=31.7815&lon=-84.3711&FcstType=digitalDWML

XML-файл имеет два разных типа узлов, которые меня интересуют.

  1. start-valid-time узлы (//start-valid-time)
  2. Атрибут coverage узлов value, у которых нет атрибута additional (//weather-conditions/value[not(@additive)]/@coverage).

Узлы связаны между собой не вложенностью, а положением. Первый узел start-valid-time соответствует первому атрибуту //weather-conditions/value[not@additive)]/@coverage.

Я хотел бы вывести start-valid-time, за которым следует запятая, за которой следует соответствующий атрибут coverage. например

2015-03-11T14:00:00-04:00, chance 2015-03-11T15:00:00-04:00, chance ... 2015-03-12T03:00:00-04:00, slight chance

Я пытался использовать различные команды xmlstarlet безрезультатно.

Вот один:

xmlstarlet sel -T  -t -m "//weather-conditions/value[not(@additive)]" -v "//start-valid-time" -v "@coverage" -n  XML 

Возможно, мне ближе всего подошла эта команда:

xmlstarlet sel -T  -t -m "//start-valid-time" -v "concat(current(),',',//weather-conditions[count(preceding-sibling::start-valid-time)+1]/value/@coverage)" -n XML

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

Я был бы признателен за помощь с этим!


person Mike Furlender    schedule 11.03.2015    source источник
comment
Эта ссылка никуда не ведет, если я не ошибаюсь.   -  person Mathias Müller    schedule 12.03.2015
comment
Странно - это сработало секунду назад. Я обновил его.. теперь он снова работает.   -  person Mike Furlender    schedule 12.03.2015


Ответы (1)


Это очень сложный вызов только в XPath, но в XSLT он предельно прост.

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="text" encoding="UTF-8" />

  <xsl:variable name="weatherCond" select="//weather-conditions/value[not(@additive)]" />

  <xsl:template match="/">
    <xsl:for-each select="//start-valid-time">
      <xsl:variable name="myPos" select="position()" />
      <xsl:value-of select="." />
      <xsl:text>,</xsl:text>
      <xsl:value-of select="$weatherCond[position() = $myPos]/@coverage" />
      <xsl:value-of select="'&#xA;'" />
    </xsl:for-each>
  </xsl:template>
</xsl:transform>

выходы

2015-03-11T17:00:00-04:00,chance
2015-03-11T18:00:00-04:00,chance
2015-03-11T19:00:00-04:00,chance
2015-03-11T20:00:00-04:00,chance
2015-03-11T21:00:00-04:00,chance
2015-03-11T22:00:00-04:00,chance
2015-03-11T23:00:00-04:00,chance
2015-03-12T00:00:00-04:00,chance
2015-03-12T01:00:00-04:00,chance
2015-03-12T02:00:00-04:00,slight chance
2015-03-12T03:00:00-04:00,slight chance
2015-03-12T04:00:00-04:00,slight chance
...

Тем не менее, я полагаю, вы также можете просто использовать две основные команды выбора xmlstarlet и объединить их выходные данные построчно.

person Tomalak    schedule 11.03.2015
comment
Это отлично. Как мне изменить @coverage, чтобы объединить оба узла значений (если существует тот, который содержит аддитивный параметр)? - person Mike Furlender; 12.03.2015
comment
Если для каждого значения может быть только один дополнительный параметр, просто используйте <xsl:if test="following-sibling::value[1][@additive]"> для его проверки, а затем выведите его с помощью <xsl:value-of> внутри if. Если их может быть больше, все становится немного сложнее. - person Tomalak; 12.03.2015
comment
Эквивалентная команда xmlstarlet должна быть xmlstarlet sel -T -t --var 'weatherCond=//weather-conditions/value[not(@additive)]' -m //start-valid-time --var 'myPos=position()' -v . -o , -v '$weatherCond[position() = $myPos]/@coverage' -n, замените " на ', если используете cmd.exe. - person npostavs; 12.03.2015
comment
@npostavs Я не понимал, что мне нужно $weatherCond[position() = $myPos] , а не $weatherCond[position()] . У меня было ощущение, что вам нужно хранить временные переменные. Спасибо! - person Mike Furlender; 12.03.2015