如何解析此XML文件中的各个元素?

时间:2011-02-22 22:39:05

标签: php simplexml

我正在使用SimpleXML从XML Web服务响应中获取数据。我们需要用碎片创建数据库记录。这是我的问题:这个XML结构化(在我看来,无论如何)非常奇怪,而且我不确定如何将所有应该包含单个记录的部分组合在一起。这是国家气象服务预报网络服务返回的数据。我们传递多个纬度/经度对,开始日期和结束日期,并要求它返回3个数据 - 风速,风向和波高。它发回的是每个纬度/长对的两个独立的位置元素 - 一个用于风信息,一个用于水(波)信息。然后,在位置元素之外,存在被列为“适用于”特定位置的参数元素。这是一个示例(很遗憾,它太长了;显示文件的结构是必要的。)

 <data> 
    <location> 
      <location-key>point1</location-key> 
      <point latitude="38.99" longitude="-77.02"/> 
    </location> 

    <location> 
      <location-key>point2</location-key> 
      <point latitude="39.70" longitude="-104.80"/> 
    </location> 

    <location> 
      <location-key>point3</location-key> 
      <point latitude="47.60" longitude="-122.30"/> 
    </location> 

    <time-layout time-coordinate="local" summarization="none"> 
      <layout-key>k-p3h-n34-1</layout-key> 
      <start-valid-time>2011-02-22T16:00:00-05:00</start-valid-time> 
      <start-valid-time>2011-02-22T19:00:00-05:00</start-valid-time> 
    </time-layout> 

    <time-layout time-coordinate="local" summarization="none"> 
      <layout-key>k-p6h-n17-2</layout-key> 
      <start-valid-time>2011-02-22T19:00:00-05:00</start-valid-time> 
      <start-valid-time>2011-02-23T01:00:00-05:00</start-valid-time> 
    </time-layout> 

    <time-layout time-coordinate="local" summarization="none"> 
      <layout-key>k-p3h-n34-3</layout-key> 
      <start-valid-time>2011-02-22T14:00:00-07:00</start-valid-time> 
      <start-valid-time>2011-02-22T17:00:00-07:00</start-valid-time> 
    </time-layout> 

    <time-layout time-coordinate="local" summarization="none"> 
      <layout-key>k-p6h-n17-4</layout-key> 
      <start-valid-time>2011-02-22T17:00:00-07:00</start-valid-time> 
      <start-valid-time>2011-02-22T23:00:00-07:00</start-valid-time> 
    </time-layout> 

    <time-layout time-coordinate="local" summarization="none"> 
      <layout-key>k-p3h-n34-5</layout-key> 
      <start-valid-time>2011-02-22T13:00:00-08:00</start-valid-time> 
      <start-valid-time>2011-02-22T16:00:00-08:00</start-valid-time> 
    </time-layout> 

    <time-layout time-coordinate="local" summarization="none"> 
      <layout-key>k-p6h-n17-6</layout-key> 
      <start-valid-time>2011-02-22T16:00:00-08:00</start-valid-time> 
      <start-valid-time>2011-02-22T22:00:00-08:00</start-valid-time> 
    </time-layout> 

    <parameters applicable-location="point1"> 
      <wind-speed type="sustained" units="knots" time-layout="k-p3h-n34-1"> 
        <name>Wind Speed</name> 
        <value>5</value> 
        <value>5</value> 
      </wind-speed> 

      <direction type="wind" units="degrees true" time-layout="k-p3h-n34-1"> 
        <name>Wind Direction</name> 
        <value>340</value> 
        <value>350</value> 
      </direction> 

      <water-state time-layout="k-p6h-n17-2"> 
        <waves type="significant" units="feet"> 
          <name>Wave Height</name> 
          <value xsi:nil="true"/> 
          <value xsi:nil="true"/> 
        </waves> 
      </water-state> 

    </parameters> 

    <parameters applicable-location="point2"> 
      <wind-speed type="sustained" units="knots" time-layout="k-p3h-n34-3"> 
        <name>Wind Speed</name> 
        <value>4</value> 
        <value>2</value> 
      </wind-speed> 
      <direction type="wind" units="degrees true" time-layout="k-p3h-n34-3"> 
        <name>Wind Direction</name> 
        <value>180</value> 
        <value>200</value> 
      </direction> 
      <water-state time-layout="k-p6h-n17-4"> 
        <waves type="significant" units="feet"> 
          <name>Wave Height</name> 
          <value xsi:nil="true"/> 
        </waves> 
      </water-state> 
    </parameters> 
    <parameters applicable-location="point3"> 
      <wind-speed type="sustained" units="knots" time-layout="k-p3h-n34-5"> 
        <name>Wind Speed</name> 
        <value>7</value> 
        <value>8</value> 
      </wind-speed> 
      <direction type="wind" units="degrees true" time-layout="k-p3h-n34-5"> 
        <name>Wind Direction</name> 
        <value>290</value> 
      </direction> 
      <water-state time-layout="k-p6h-n17-6"> 
        <waves type="significant" units="feet"> 
          <name>Wave Height</name> 
          <value xsi:nil="true"/> 
        </waves> 
      </water-state> 
    </parameters> 
  </data>

我需要最终得到的是每个位置的三个SQL插入语句,如下所示:

INSERT into gl_weather_data (weather_lat, weather_long, weather_time, weather_type, weather_value, weather_unit) values ("46.72", "-91.82", "2011-02-22T12:00:00-06:00", "Wind Speed", "9", "knots") 

INSERT into gl_weather_data (weather_lat, weather_long, weather_time, weather_type, weather_value, weather_unit) values ("46.72", "-91.82", "2011-02-22T12:00:00-06:00", "Wind Direction", "120", "degrees true") 

INSERT into gl_weather_data (weather_lat, weather_long, weather_time, weather_type, weather_value, weather_unit) values ("46.72", "-91.82", "2011-02-22T12:00:00-06:00", "Wave Height", "2", "feet") 

lat / long / time的数据来自这些元素,并且对于所有三个插入都是通用的:

<location>
    <location-key>point1</location-key>
    <point latitude="46.72" longitude="-91.82" />
</location>

<time-layout time-coordinate="local" summarization="none"> 
  <layout-key>k-p3h-n34-1</layout-key> 
  <!-- note there can be more than one start-valid-time elements; we only want the first one -->
  <start-valid-time>2011-02-22T16:00:00-05:00</start-valid-time> 
  <start-valid-time>2011-02-22T19:00:00-05:00</start-valid-time> 
</time-layout> 

类型,值和单位的数据来自不同的元素,具体取决于我们获得的信息类型:

<!-- wind speed; need to get "knots" out of the wind-speed element's unit param,
     "Wind Speed" out of the name element, and "5" out of the value element 
     (note there can be more than one value element; we only want the first)  -->
<parameters applicable-location="point1"> 
  <wind-speed type="sustained" units="knots" time-layout="k-p3h-n34-1"> 
    <name>Wind Speed</name> 
       <value>5</value> 
       <value>5</value> 
  </wind-speed> 

<!-- wind direction; need to get "degrees true" out of the direction element's unit param,
     "Wind Direction" out of the name element, and "340" out of the value element 
     (note there can be more than one value element; we only want the first)  -->
 <direction type="wind" units="degrees true" time-layout="k-p3h-n34-1"> 
        <name>Wind Direction</name> 
        <value>340</value> 
        <value>350</value> 
      </direction> 

<!-- wave height; need to get "feet" out of the waves element's unit param,
     "Wave Height" out of the name element, and "13" out of the value element 
     (note there can be more than one value element; we only want the first)  -->
 <water-state time-layout="k-p6h-n17-2"> 
        <waves type="significant" units="feet"> 
          <name>Wave Height</name> 
          <value>13</value> 
          <value xsi:nil="true"/> 
        </waves> 
      </water-state> 

我的主要困惑是如何将位置元素与其对应的参数元素相关联;如果我已经编写了模式,我可能会将参数设置为位置的子项,但这不是它如何来到我们这里,显然我们不能轻易地改变它。

我猜我可能需要为每个位置元素做一个,获取第一个的位置键,然后以某种方式使用该位置键通过将其与适用的参数匹配来选择正确的参数元素,但我不知道如何使用SimpleXML。有人可以帮我从这里出去吗?

编辑添加工作/非工作代码

这很有效 - 它远不是我需要做的,但至少我得到了一个结果:

$dwml = simplexml_load_string($result);
foreach ($dwml->data->parameters as $r) {
  $locName = $r->direction['type'];
  echo "Name:  $locName<br />";
}

foreach ($dwml->data->location as $r) {
  echo "location key: " . $r->{'location-key'} . "<br />";
}

这不起作用:

$data = simplexml_load_string($result);
    $all_locations = $data->xpath('location');
    foreach( $all_locations as $location ) {
       list($location_key) = $location->xpath('location-key[1]');

       $params = $data->xpath("parameters[@applicable-location='{$location_key}']/*");
       foreach( $params as $param ) {
          list($time) = $data->xpath("time-layout[layout-key='{$param['time-layout']}']/start-valid-time[1]");
          if( $param->getName() == 'water-state' ) {
             $param = $param->waves;
          }
          $sql = "INSERT into gl_weather_data values ('{$location->point['latitude']}', '{$location->point['longitude']}', '{$time}', '{$param->name}', '{$param->value[0]}', '{$param['units']}')";
          echo "{$sql}\n\n";
       }
    }

再次编辑 好吧,我想我明白了 - 这就是我真正有用的东西(dwml实际上是根元素,而不是数据):

$dwml = simplexml_load_string($result);
    $all_locations = $dwml->data->xpath('location');
    foreach( $all_locations as $location ) {
       list($location_key) = $location->xpath('location-key[1]');

       $params = $dwml->data->xpath("parameters[@applicable-location='{$location_key}']/*");
       foreach( $params as $param ) {
          list($time) = $dwml->data->xpath("time-layout[layout-key='{$param['time-layout']}']/start-valid-time[1]");
          if( $param->getName() == 'water-state' ) {
             $param = $param->waves;
          }
          $sql = "INSERT into gl_weather_data values ('{$location->point['latitude']}', '{$location->point['longitude']}', '{$time}', '{$param->name}', '{$param->value[0]}', '{$param['units']}')";
          echo "{$sql}\n\n";
       }
    }

1 个答案:

答案 0 :(得分:2)

我建议您使用SimpleXML's XPath功能。

你最终会得到这样的东西:

$data = new SimpleXMLElement($string);

$all_locations = $data->xpath('location');
foreach( $all_locations as $location ) {
   list($location_key) = $location->xpath('location-key[1]');

   $params = $data->xpath("parameters[@applicable-location='{$location_key}']/*");
   foreach( $params as $param ) {
      list($time) = $data->xpath("time-layout[layout-key='{$param['time-layout']}']/start-valid-time[1]");
      if( $param->getName() == 'water-state' ) {
         $param = $param->waves;
      }
      $sql = "INSERT into gl_weather_data values ('{$location->point['latitude']}', '{$location->point['longitude']}', '{$time}', '{$param->name}', '{$param->value[0]}', '{$param['units']}')";
      echo "{$sql}\n\n";
   }
}

修改

上面的示例查询所有<location>元素,然后找到与每个位置一起的<parameters>部分。在我看来,您可能更愿意以另一种方式接近它 - 找到所有<parameters>元素,然后查找相关的<location>

使用XPath,这种变化非常简单:

$data = new SimpleXMLElement($string);
$all_params = $data->xpath("parameters");
foreach( $all_params as $paramblock ) {
   list($location) = $data->xpath("location[location-key='{$paramblock['applicable-location']}']");

   foreach( $paramblock->children() as $item ) {
      list($time) = $data->xpath("time-layout[layout-key='{$item['time-layout']}']/start-valid-time[1]");
      if( $item->getName() == 'water-state' ) {
         $item = $item->waves;
      }
      $sql = "INSERT into gl_weather_data values ('{$location->point['latitude']}', '{$location->point['longitude']}', '{$time}', '{$item->name}', '{$item->value[0]}', '{$item['units']}')";
      echo "{$sql}\n\n";
   }
}