Я пытаюсь прочитать большой файл XML с помощью XMLReader и не могу найти способ правильно перебрать поддерево.
До сих пор я пытался использовать функции read() и next(). И это не работает должным образом. Вот структура XML, которую я анализирую:
<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>
Как видите, узел TELEFONES может иметь несколько узлов TELEFONE. Поэтому мне нужно зациклить это и получить их по отдельности. Пока это мой код:
$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();
У меня тут две проблемы. Во-первых, мой последний массив содержит информацию только об одном узле CLIENTE. В нем должны быть все узлы CLIENTE, я индексирую их с помощью $cont var.
Другая проблема заключается в том, что узел TELEFONES, который идет к моему массиву $clientes, принадлежит последнему узлу CLIENTE XML. Итак, каким-то образом мой код проходит через каждый узел CLIENTE, но когда я обрабатываю узел TELEFONES, мой массив $clientes становится все испорченным.
Я просто не могу найти способ зациклить поддерево с помощью XMLParser. Кто-нибудь может мне помочь?






Вместо того, чтобы пытаться прочитать весь документ поэлементно, вы можете с помощью XMLReader попросить его импортировать сегменты.
В этом примере кода, как только вы доберетесь до уровня <CLIENTE>, он считывает все элементы этого уровня в SimpleXMLElement (используя simplexml_import_dom()). После того, как вы это сделаете, вы можете обрабатывать каждый из них, используя более простой интерфейс, и вам не придется иметь дело с начальными и конечными тегами и т. д.
$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;
}
}
}
}
Это предполагает, что каждый <CLIENTE> не очень велик. Возможно, вам также придется следить за тем, чтобы массив $clientes не стал слишком большим.
Этот метод подходит для большинства задач, он будет обрабатывать узлы CLIENTE со 100 (если не 1000) узлами. Делает обработку каждого намного проще.
Это сработало, Найджел Рен, спасибо. Мой файл XML очень большой, в нем много узлов CLIENTE. Узел CLIENTE имеет внутри себя больше узлов, всего 25 узлов, 2 из них с той же структурой, что и TELEFONE, с дочерними элементами. Это плохо, если я буду продолжать использовать ваш метод для анализа данных?