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

发布于 2024-10-18 10:29:11 字数 10422 浏览 1 评论 0原文

我正在使用 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") 

The data for the 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> 

我的主要困惑是如何将位置元素与其对应的参数元素;如果我编写了模式,我可能会将参数设置为位置的子项,但这不是我们的方式,显然我们不能轻易更改它。

我猜我可能需要对位置元素执行 for-each 操作,获取第一个位置元素的位置键,然后以某种方式使用该位置键通过将其与参数适用位置匹配来选择正确的参数元素,但我不知道如何使用 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";
       }
    }

I'm using SimpleXML to get pieces of data from an XML web-service response. We need to create database records with the pieces. Here's my issue: this XML is structured (in my mind, anyway) very strangely, and I'm not sure how to get all the pieces that should comprise a single record together. This is data returned by the National Weather Services forecast web service. We are passing in multiple latitude/longitude pairs, a start date, and and end date, and asking it to return 3 pieces of data - wind speed, wind direction, and wave height. What it sends back is two separate location elements for each lat/long pair - one for the wind information, one for the water (wave) information. Then, outside of the location elements, there are parameters elements that are listed as "applicable" to a specific location. Here's a sample (sorry it's so long; it's kind of necessary to show how the file is structured.)

 <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>

What I need to end up with is three SQL insert statements for each location that looks something like this:

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") 

The data for the lat/long/time would come from these elements and are common to all three inserts:

<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> 

The data for type, value, and unit come from different elements depending on what type of information we're getting:

<!-- 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> 

My main confusion is how to associate the location elements with their corresponding parameters elements; if I had written the schema I probably would have made parameters a child of location, but that's not how it's coming to us, and obviously we can't easily change it.

I'm guessing probably I need to do a for-each on location elements, get the location key of the first one, then somehow use that location key to select the correct parameters element by matching it to the parameters applicable-location, but I have no idea how to do this with SimpleXML. Can anyone help me out here?

EDITED TO ADD WORKING/NON-WORKING CODE

This works - it's nowhere near to what I need to do, but at least I get a result:

$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 />";
}

This doesn't work:

$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";
       }
    }

EDITED AGAIN
OK, I think I got it - here's what actually works for me (dwml is actually the root element, not data):

$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";
       }
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

滥情稳全场 2024-10-25 10:29:11

我建议您使用 SimpleXML 的 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";
   }
}

edit

上面的示例查询所有 元素,然后找到 与每个位置相关的部分。我认为您可能更喜欢以其他方式处理它 - 找到所有 元素,然后查找相关的

使用 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";
   }
}

I would suggest that you use SimpleXML's XPath functionality.

You'll end up with something like this:

$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";
   }
}

edit

The example above queries for all the <location> elements, then finds the <parameters> section that goes with each location. It occurs to me that you might prefer to approach it the other way -- find all the <parameters> elements, then look up the related <location>.

Using XPath, this change is fairly simple:

$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";
   }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文