dongyu4908
dongyu4908
2019-04-22 14:31

使用XMLReader和PHP获取大型XML文件中的子树数据

已采纳

I'm trying to read a big XML file using XMLReader and I can't find a way to loop through a subtree correctly.

So far, I tried to use the read() and the next() functions. And it's not working properly. Here is the XML structure that I'm parsing:

<CLIENTES>
<CLIENTE>
        <CODIGO_INTERESSADO>10</CODIGO_INTERESSADO>
        <NOME_INTERESSADO>Pedro</NOME_INTERESSADO>
        <ENDERECO />
        <COMPLEMENTO />
        <ESTADO />
        <MUNICIPIO />
        <BAIRRO />
        <CEP />
        <DATA_CADASTRO>16/09/2015</DATA_CADASTRO>
        <STATUS>Ativo</STATUS>
        <TELEFONES>
            <TELEFONE>
                <NUMERO>(21) 96909-6905</NUMERO>
                <TIPO>Celular</TIPO>
            </TELEFONE>
        </TELEFONES>
    </CLIENTE>
<CLIENTE>
        <CODIGO_INTERESSADO>11</CODIGO_INTERESSADO>
        <NOME_INTERESSADO>Luiz</NOME_INTERESSADO>
        <ENDERECO />
        <COMPLEMENTO />
        <ESTADO />
        <MUNICIPIO />
        <BAIRRO />
        <CEP />
        <DATA_CADASTRO>16/09/2015</DATA_CADASTRO>
        <STATUS>Ativo</STATUS>
        <TELEFONES>
            <TELEFONE>
                <NUMERO>(21) 96909-6901</NUMERO>
                <TIPO>Celular</TIPO>
            </TELEFONE>
        </TELEFONES>
    </CLIENTE>
</CLIENTES>

As you can see, the node TELEFONES, can have multiple TELEFONE nodes. So I need to loop that and get them individually. So far, this is my code:

$xml = new XMLReader();

$xml->open('xml_formatado_stack.xml');

$cont = 0;
$clientes = array();
while ($xml->read()) {

    if ($xml->nodeType == XMLReader::ELEMENT && $xml->localName == 'CLIENTES') {
        while ($xml->read()) {
            if ($xml->nodeType == XMLReader::ELEMENT && $xml->localName == 'CLIENTE') {

                while ($xml->read()) {
                    $telefone = array();
                    if($xml->nodeType == XMLReader::ELEMENT) {
                        if($xml->localName == 'CODIGO_INTERESSADO') {
                            $xml->read(); 
                            echo $xml->value."<br>";
                            $clientes[$cont]['codigo_interessado'] = $xml->value;                                                       
                        }

                        if($xml->localName == 'NOME_INTERESSADO') {
                            $xml->read(); 
                            $clientes[$cont]['nome_interessado'] = $xml->value;
                        }

                        if($xml->localName == 'ENDERECO') {
                            $xml->read(); 
                            $clientes[$cont]['endereco'] = $xml->value;
                        }

                        if($xml->localName == 'COMPLEMENTO') {
                            $xml->read(); 
                            $clientes[$cont]['complemento'] = $xml->value;
                        }

                        if($xml->localName == 'ESTADO') {
                            $xml->read(); 
                            $clientes[$cont]['estado'] = $xml->value;
                        }

                        if($xml->localName == 'MUNICIPIO') {
                            $xml->read(); 
                            $clientes[$cont]['municipio'] = $xml->value;
                        }

                        if($xml->localName == 'BAIRRO') {
                            $xml->read(); 
                            $clientes[$cont]['bairro'] = $xml->value;
                        }

                        if($xml->localName == 'CEP') {
                            $xml->read(); 
                            $clientes[$cont]['cep'] = $xml->value;
                        }


                        if($xml->localName == 'DATA_CADASTRO') {
                            $xml->read(); 
                            $clientes[$cont]['data_cadastro'] = $xml->value;
                        }

                        if($xml->localName == 'STATUS') {
                            $xml->read(); 
                            $clientes[$cont]['status'] = $xml->value;                           
                        }

                        if ($xml->localName == 'TELEFONES') {
                            while ($xml->read()) {
                                if ($xml->nodeType == XMLReader::ELEMENT && $xml->localName == 'TELEFONE') {
                                    while ($xml->read()) {
                                        if($xml->nodeType == XMLReader::ELEMENT) {
                                            if($xml->localName == 'NUMERO') {
                                                $xml->read(); 
                                                $telefone['numero'] = $xml->value;                                              
                                            }

                                            if($xml->localName == 'TIPO') {
                                                $xml->read(); 
                                                $telefone['tipo'] = $xml->value;
                                            }
                                        }
                                    }
                                }
                            }                           
                            $clientes[$cont]['telefones'][] = $telefone;
                            $cont++;
                        }                       
                    }

                }
            }
        }

    }
}

var_dump($clientes);

$xml->close();

I'm getting two problems here. First, my final array is having information about only one CLIENTE node. It should have all the CLIENTE nodes, I'm indexing them with the $cont var.

The other problem is that, the TELEFONES node that is going to my $clientes array belongs to the last CLIENTE node of the XML. So, somehow my code is going through every CLIENTE node, but when I treat the TELEFONES node, my $clientes array is getting all messed up.

I just can't find a way to loop a subtree using XMLParser. Can someone help me?

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

1条回答

  • douyao5533 douyao5533 2年前

    Rather than trying to read the whole document element by element, you can with XMLReader ask it to import segments.

    In this example code, once you get to the <CLIENTE> level, it reads all of the elements of that level into a SimpleXMLElement (using simplexml_import_dom()). Once you have done this, you can then process each one using the simpler interface and not have to deal with start and end tags etc...

    $xml = new XMLReader();
    
    $xml->open('xml_formatado_stack.xml');
    
    $clientes = array();
    $doc = new DOMDocument;
    while ($xml->read()) {
    
        if ($xml->nodeType == XMLReader::ELEMENT && $xml->localName == 'CLIENTES') {
            while ($xml->read()) {
                if ($xml->nodeType == XMLReader::ELEMENT && $xml->localName == 'CLIENTE') {
                    // Import all child elements into $cl
                    $cl = simplexml_import_dom($doc->importNode($xml->expand(), true));
                    // Extract each piece of data, i.e. $cl->CODIGO_INTERESSADO and convert to string to store it
                    $cliente = [ 'codigo_interessado' => (string)$cl->CODIGO_INTERESSADO,
                        'nome_interessado' => (string)$cl->NOME_INTERESSADO,
    
                        // You will need to complete this bit
    
                    ];
                    // Loop across each of the TELEFONE records and store them
                    foreach ( $cl->TELEFONES->TELEFONE as $telefone )   {
                        $cliente['telefones'][] = ['numero' => (string)$telefone->NUMERO,
                            'tipo' => (string)$telefone->TIPO
                        ];
                    }
                    // Add the new data to the overall list
                    $clientes[] = $cliente;
                }
            }
    
        }
    }
    

    This does assume that each <CLIENTE> isn't very large. You may also have to be careful that the array $clientes doesn't become too large.

    点赞 评论 复制链接分享