3

SimpleXML を使用して xml ドキュメントを繰り返し処理しています。ID を持つ配列 ($ids) があり、XML (Worksheet/Table/Row/Cell/Data) に一致があるかどうかを確認しています。一致する場合は、次の 2 つの兄弟からデータを取得できるようにしたいのですが、方法がわかりません。

PHPから:

// $ids <---- array('8', '53', '38')

foreach ($thePositions->Worksheet->Table->Row as $row) {

    if($row->Cell->Data == true) {

        for ($i = 0; $i < count($ids); $i++) {
            foreach($row->Cell->Data as $data) {

                if ($data == $ids[$i]) {
                    echo 'match!';

                    /* 
                       Tried $siblings = $data->xpath('preceding-sibling::* | following-sibling::*');
                       but doesn't seem to work in this case.
                    */
                }
            }
        }
    }
}

xml:

<?xml version="1.0"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
 xmlns:o="urn:schemas-microsoft-com:office:office"
 xmlns:x="urn:schemas-microsoft-com:office:excel"
 xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
 xmlns:html="http://www.w3.org/TR/REC-html40">
 <DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
  <LastAuthor>Herpa Derp </LastAuthor>
  <Created>2012-09-25T13:44:01Z</Created>
  <LastSaved>2012-09-25T13:48:24Z</LastSaved>
  <Version>14.0</Version>
 </DocumentProperties>
 <OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">
  <AllowPNG/>
 </OfficeDocumentSettings>
 <ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
  <WindowHeight>14060</WindowHeight>
  <WindowWidth>25040</WindowWidth>
  <WindowTopX>25540</WindowTopX>
  <WindowTopY>4100</WindowTopY>
  <Date1904/>
  <ProtectStructure>False</ProtectStructure>
  <ProtectWindows>False</ProtectWindows>
 </ExcelWorkbook>
 <Styles>
  <Style ss:ID="Default" ss:Name="Normal">
   <Alignment ss:Vertical="Bottom"/>
   <Borders/>
   <Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000"/>
   <Interior/>
   <NumberFormat/>
   <Protection/>
  </Style>
  <Style ss:ID="s62">
   <Font ss:FontName="Courier" ss:Color="#000000"/>
  </Style>
 </Styles>
 <Worksheet ss:Name="Workbook1.csv">
  <Table ss:ExpandedColumnCount="5" ss:ExpandedRowCount="79" x:FullColumns="1"
   x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="15">
   <Column ss:Index="2" ss:AutoFitWidth="0" ss:Width="43"/>
   <Column ss:AutoFitWidth="0" ss:Width="113"/>
   <Column ss:Index="5" ss:AutoFitWidth="0" ss:Width="220"/>
   <Row ss:Index="6">
    <Cell ss:Index="3" ss:StyleID="s62"/>
   </Row>
   <Row>
    <Cell ss:Index="3" ss:StyleID="s62"/>
   </Row>
   <Row>
    <Cell ss:Index="3" ss:StyleID="s62"/>
   </Row>
   <Row>
    <Cell ss:Index="2"><Data ss:Type="String">id</Data></Cell>
    <Cell ss:StyleID="s62"><Data ss:Type="String">latitude</Data></Cell>
    <Cell><Data ss:Type="String">longitude</Data></Cell>
   </Row>
   <Row>
    <Cell ss:Index="2"><Data ss:Type="Number">8</Data></Cell>
    <Cell ss:StyleID="s62"><Data ss:Type="Number">57.4999</Data></Cell>    // to be saved to $latutude
    <Cell><Data ss:Type="Number">15.8280</Data></Cell>    // to be saved to $longitude
   </Row>
   <Row>
    <Cell ss:Index="2"><Data ss:Type="Number">38</Data></Cell>
    <Cell><Data ss:Type="Number">56.5659</Data></Cell>
    <Cell><Data ss:Type="Number">16.1380</Data></Cell>
   </Row>
4

4 に答える 4

1

兄弟の要求が機能しない理由は、<Data>要素が兄弟ではないためです。彼らはいとこのようなものです - 隣接する<Cell>要素の子供です。

同じ理由で、 を使用しないでください。foreach($row->Cell->Data as $data)これは と同等です。つまり、最初のノードのforeach($row->Cell[0]->Data as $data)すべての<Data>子を調べます。a には要素<Cell>が 1 つしかないため、次のように書くこともできます。探している値は行の先頭にあるため、この場合は問題ありません。<Data><Cell>$data = $row->Cell[0]->Data

実際に行う必要があるのは、<Cell>sをループすることです。foreach($row->Cell as $cell) { $data = $cell->Data; /* ... */ }

次に、XPath など、隣接するセルを見つけるためのオプションがいくつかあります。より「PHPっぽい」方法は、配列インデックスを使用することです(兄弟はSimpleXMLループ/配列アクセスで数値的にインデックス付けされます):

foreach($row->Cell as $cell_index => $cell)
{
    $data = $cell->Data;
    if ($data == $ids[$i])
    {
        // Tip: always cast SimpleXML objects to string when you're done with their magic XMLiness
        $latitudes[$i] = (string)$row->Cell[ $cell_index + 1 ]->Data;
        $longitudes[$i] = (string)$row->Cell[ $cell_index + 2 ]->Data;
    }
}

または、ID が常に最初の列にあり、緯度と経度が次の 2 列にあることに依存し (これは結局スプレッドシートです!)、内部ループを完全に回避することもできます。

if ( $row->Cell[0]->Data == $ids[$i] )
{
    $latitudes[$i] = (string)$row->Cell[1]->Data;
    $longitudes[$i] = (string)$row->Cell[2]->Data;
}
于 2012-12-05T00:00:36.267 に答える
1

この XML の場合、セルは常に同じ順序になっているように見えるため、次のように実行できます。

$ids = array('8', '53', '38');
foreach ($xml->Worksheet->Table->Row as $row) {
    $children = $row->children();
    if (count($children) == 3 && in_array(((string) $children[0]->Data), $ids)) {
        echo 'lat: ' . $children[1]->Data . ' lng: ' . $children[2]->Data . "\n";
    }
}
于 2012-12-04T23:36:03.463 に答える
0

次のようなループなしで、XPathで完全に実行できます。

//Row[Cell/Data[. = '8' or . = '53' or . = '38']]/following-sibling::*[position() <= 2]

これにより、任意のデータ要素のIDを持つすべての行が検索され、次の2つの兄弟が取得されます。

または

//Row[Cell[1]/Data[. = '8' or . = '53' or . = '38']]/following-sibling::*[position() <= 2]

IDが常に最初のセルにあることが確実な場合。(これにより、IDが経度/経度と同じであるためのエラーも防止されます)

または

//Row[Cell[@ss:Index = "2"]/Data[. = '8' or . = '53' or . = '38']]/following-sibling::*[position() <= 2]

IDがインデックス2のセルにある場合。

ただし、すべての場合において、名前空間を正しく初期化する必要があります

于 2012-12-05T00:47:33.457 に答える
0

一致する ID が多数ある場合の別の方法は、ID に基づいてすべての行の「ハッシュ」を作成し、一致を検索するループではなく、そのハッシュを調べることです。

// Initialise an empty array to use as the hash
$rows_by_id = array();

// Loop over all the rows in the spreadsheet
foreach ($thePositions->Worksheet->Table->Row as $row) {
    // Skip rows with less than 3 cells
    if ( count($row->Cell) < 3 ) {
        continue;
    }

    // Take the ID from the first cell on the row
    $id = (string)$row->Cell[0]->Data;

    // Add this row to the hash, identified by it's ID
    $rows_by_id[$id] = array(
        'latitude'  => (string)$row->Cell[1]->Data,
        'longitude' => (string)$row->Cell[2]->Data
    );

    // Or if your IDs are not unique, and you want all matches:
    // $rows_by_id[$id][] = array( ... )
}

foreach ( $ids  as $required_id ) {
    // Retrieve the results from the hash
    if ( isset($rows_by_id[$required_id]) ) { 
        $matched_row = $rows_by_id[$required_id];

        echo "ID $required_id has latitude {$matched_row['latitude']} and longitude {$matched_row['longitude']}.\n";
    }
    else {
        echo "ID $required_id was not matched. :(\n";
    }

    // If you have non-unique matches, you'll need something like this:
    // $all_matched_rows = $rows_by_id[$required_id]; ... foreach ( $all_matched_rows as $matched_row )
}
于 2012-12-07T14:42:32.243 に答える