PHP XPath. Конвертировать сложный XML в массив
После трех дней попыток решить это самостоятельно я должен сдаться. Это выдержка из огромного XML-файла, экспортированного из дБ итальянских законов. Я хотел бы преобразовать этот XML в массив PHP, так как цель состоит в том, чтобы использовать этот массив для построения отформатированного документа Word с использованием класса PHP как PHP2RTF.
<CodiceRegionale>
<sommario>
<elementoCapitolo>CAP a1</elementoCapitolo>
<elementoCapitoloDescr>
</elementoCapitoloDescr>
<elementoSommarioLegge>
<elementoTesto>1.1 Legge</elementoTesto>
</elementoSommarioLegge>
</sommario>
<LeggeRegionale id="urn:nir:2014-12-12;26" xmlns="http://www.normeinrete.it/nir/2.1/">
<elementoSommario xmlns="">
<elementoCapitolo>CAP a1</elementoCapitolo>
<elementoCapitoloDescr>
</elementoCapitoloDescr>
<elementoSettore />
<elementoSettoreDescr />
</elementoSommario>
<intestazione>Lex 12 2014, n. 26.</intestazione>
<articolato>
<articolo id="art41" xmlns="http://www.normeinrete.it/nir/2.1/">
<num>Art. 41</num>
<rubrica>(Riforma della finanza locale)</rubrica>
<comma id="art41-com1">
<num>1. </num>
<alinea>Al fine di supportare...</alinea>
<el id="art41-com1-let_a">
<num>a) </num>
<corpo>definizione di...</corpo>
</el>
<el id="art41-com1-let_b">
<num>b) </num>
<corpo>coordinamento della...</corpo>
</el>
<el id="art41-com1-let_c">
<num>c) </num>
<corpo>definizione delle...</corpo>
</el>
<el id="art41-com1-let_d">
<num>d) </num>
<corpo>la disciplina...</corpo>
</el>
</comma>
<comma id="art41-com2">
<num>2. </num>
<alinea>La revisione di...</alinea>
<el id="art41-com2-let_a">
<num>a) </num>
<corpo>
razionalizzazione e...
<rif xlink:href="urn:nir:stato:legge:2010-12-13;220#art1-com154" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink">articolo 1, comma 154, della legge 13 dicembre 2010, n. 220</rif>
(Legge di stabilità 2011);
</corpo>
</el>
<el id="art41-com2-let_b">
<num>b) </num>
<corpo>
applicazione dei...
<rif xlink:href="urn:nir:stato:costituzione:1947-12-27#art119" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink">articolo 119 della Costituzione</rif>
, nonché del principio...
</corpo>
</el>
<el id="art41-com2-let_c">
<num>c) </num>
<corpo>valorizzazione...</corpo>
</el>
<el id="art41-com2-let_d">
<num>d) </num>
<corpo>previsione di...</corpo>
</el>
<el id="art41-com2-let_e">
<num>e) </num>
<corpo>valorizzazione del...</corpo>
</el>
<el id="art41-com2-let_f">
<num>f) </num>
<corpo>previsione di...</corpo>
</el>
</comma>
<comma id="art41-com3">
<num>3. </num>
<corpo>La revisione normativa...</corpo>
</comma>
<comma id="art41-com4">
<num>4. </num>
<corpo>I disegni di...</corpo>
</comma>
</articolo>
<articolo id="art42" xmlns="http://www.normeinrete.it/nir/2.1/">
<num>Art. 42</num>
<rubrica>(Supporto finanziario regionale agli enti locali)</rubrica>
<comma id="art42-com1">
<num>1. </num>
<corpo>Il supporto...</corpo>
</comma>
<comma id="art42-com2">
<num>2. </num>
<corpo>Per le finalità di...</corpo>
</comma>
<comma id="art42-com3">
<num>3. </num>
<corpo>Gli incentivi regionali...</corpo>
</comma>
<comma id="art42-com4">
<num>4. </num>
<corpo>
In attuazione...
<rif xlink:href="urn:nir:stato:decreto.legislativo:1997-01-02;9#art9" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink">articolo 9 ...</rif>
(Norme di attuazione dello
<rif xlink:href="urn:nir:regione.friuli.venezia.giulia:statuto:" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink">statuto</rif>
speciale...
</corpo>
</comma>
<comma id="art42-com5">
<num>5. </num>
<corpo>Le modalità...</corpo>
</comma>
</articolo>
</articolato>
</LeggeRegionale>
<LeggeRegionale id="urn:nir:2015-05-22;12" xmlns="http://www.normeinrete.it/nir/2.1/">
<elementoSommario xmlns="">
<elementoCapitolo>CAP a7</elementoCapitolo>
<elementoCapitoloDescr>
</elementoCapitoloDescr>
<elementoSettore />
<elementoSettoreDescr />
</elementoSommario>
<intestazione>Lex 22 2015 n. 12...</intestazione>
<articolato>
<articolo id="art6">
<num>Art. 6</num>
<rubrica>(Regolamento interno del CAL)</rubrica>
<comma id="art6-com1">
<num>1. </num>
<corpo>Il CAL approva...</corpo>
</comma>
<comma id="art6-com2">
<num>2. </num>
<alinea>Il regolamento...</alinea>
<el id="art6-com2-let_a">
<num>a) </num>
<corpo>l'elezione...</corpo>
</el>
<el id="art6-com2-let_b">
<num>b) </num>
<corpo>le funzioni degli organi del CAL;</corpo>
</el>
<el id="art6-com2-let_c">
<num>c) </num>
<corpo>la costituzione...</corpo>
</el>
<el id="art6-com2-let_d">
<num>d) </num>
<corpo>la programmazione...</corpo>
</el>
<el id="art6-com2-let_e">
<num>e) </num>
<corpo>i casi nei...</corpo>
</el>
<el id="art6-com2-let_f">
<num>f) </num>
<corpo>le modalità di...</corpo>
</el>
</comma>
<comma id="art6-com3">
<num>3. </num>
<corpo>Il regolamento è pubblicato...</corpo>
</comma>
</articolo>
</articolato>
</LeggeRegionale>
<LeggeRegionale id="urn:nir:2014-12-12;26" xmlns="http://www.normeinrete.it/nir/2.1/">
<elementoSommario xmlns="">
<elementoCapitolo>CAP a8</elementoCapitolo>
<elementoCapitoloDescr>
</elementoCapitoloDescr>
<elementoSettore />
<elementoSettoreDescr />
</elementoSommario>
<intestazione>Lex 12 2014, n. 26....</intestazione>
<articolato>
<articolo id="art17">
<num>Art. 17</num>
<rubrica>(Piano dell'Unione)</rubrica>
<comma id="art17-com1">
<num>1. </num>
<corpo>Il Piano dell'Unione...</corpo>
</comma>
<comma id="art17-com2">
<num>2. </num>
<corpo>
Il Piano ...
<rif xlink:href="urn:nir:stato:decreto.legislativo:2000;267#art170" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink">articolo 170 ...</rif>
.
</corpo>
</comma>
<comma id="art17-com3">
<num>3. </num>
<corpo>Il Piano dell'Unione...</corpo>
</comma>
<comma id="art17-com4">
<num>4. </num>
<corpo>La relazione annuale...</corpo>
</comma>
</articolo>
</articolato>
</LeggeRegionale>
<LeggeRegionale id="urn:nir:regione.friuli.venezia.giulia:legge:2014-12-12;26" xmlns="http://www.normeinrete.it/nir/2.1/">
<elementoSommario xmlns="">
<elementoCapitolo>CAP a14</elementoCapitolo>
<elementoCapitoloDescr>
</elementoCapitoloDescr>
<elementoSettore />
<elementoSettoreDescr />
</elementoSommario>
<intestazione>Lex 12 dicembre 2014, n. 26 ....</intestazione>
<articolato>
<articolo id="art5">
<num>Art. 5</num>
<rubrica>(Unioni territoriali intercomunali)</rubrica>
<comma id="art5-com1">
<num>1. </num>
<corpo>Le Unioni territoriali...</corpo>
</comma>
<comma id="art5-com2">
<num>2. </num>
<corpo>
L'Unione ha...
<rif xlink:href="urn:nir:stato:decreto.legislativo:2000-08-18;267#art32" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink">articolo 32...</rif>
(Testo unico delle leggi sull'ordinamento degli enti locali).
</corpo>
</comma>
</articolo>
</articolato>
</LeggeRegionale>
Массив, который я пытаюсь построить, должен быть похож на следующее:
Array
(
[LeggeRegionale] => Array
(
[intestazione] => Lex 12 2014, n. 26.
[articolato] => Array
(
[num] => Art. 41
[articolo] => Array
(
[rubrica] => (Riforma della finanza locale)
[commi] => Array
(
[num_alinea] => 1. Al fine di supportare...
[el] => Array
(
[num_corpo] => a) definizione di...
[num_corpo] => b) coordinamento della...
)
[num_alinea] => 2. La revisione di...
)
)
[num] => Art. 42
...
)
)
)
С помощью этого кода я могу читать ВСЕ XML, но он абсолютно бесполезен:
$results = array();
$query = '//a:LeggeRegionale';
$result = $xpath->query($query);
$results = array();
for($i=0; $i < $result->length; $i++) {
$results[$i] = $result->item($i)->nodeValue;
echo $results[$i]."<BR><BR>";
}
echo "<BR>";
Этот код возвращает что-то структурированное (хотя это не массив, который я ищу), но я не понимаю, почему он возвращает только последнее вхождение LeggeRegionale
когда я использовал foreach loop
,
foreach ($result as $entry) {
$result = [
'intestazione' => $xpath->query('//a:LeggeRegionale')->item($i)->nodeValue,
'articoli' => array(
'numero' => $xpath->query('//a:articolato/a:articolo/a:num')->item($i)->nodeValue,
'rubrica' => $xpath->query('//a:articolato/a:articolo/a:rubrica')->item($i)->nodeValue,
'commi' => array(
'numero' => $xpath->query('//a:articolato/a:articolo/a:comma/a:num')->item($i)->nodeValue,
'corpo' => $xpath->query('//a:articolato/a:articolo/a:comma/a:corpo|//a:articolato/a:articolo/a:comma/a:alinea')->item($i)->nodeValue,
'lettere' => array(
'lettera' => $xpath->query('//a:articolato/a:articolo/a:comma/a:el')->item($i)->nodeValue,
),
),
),
];
array_push($a, $result);
$i++;
}
Я думаю, что есть лучший способ - благодаря XPath - читать всех потомков, а не писать foreach loop
для каждого уровня XML.
1 ответ
Преобразование сложного XML в массив приведет к... сложному массиву. Вам трудно читать XML, и вы думаете, что массивы намного проще для вас, поэтому массив должен быть решением. Но на самом деле XML хорошо подходит для анализа дерева в PHP. Массив не так доступен. Например, вы не можете запустить запрос xpath для массива.
А для массива в вашем псевдомассиве есть ошибка, что у вас есть дубликаты ключей. Структура больше похожа на:
Array
(
[LeggeRegionale] => Array
(
[0] => Array
(
[intestazione] => Lex 12 2014, n. 26.
[articolato] => Array
(
[articolo] => Array
(
[0] => Array
(
[num] => Art. 41
[rubrica] => (Riforma della finanza locale)
[commi] => Array
(
[0] => Array
(
[num_alinea] => 1. Al fine di supportare...
[num_corpo] => Array
(
[0] => a) definizione di...
[1] => b) coordinamento della...
[2] => c) definizione delle...
[3] => d) la disciplina...
)
)
[1] => Array
(
[num_alinea] => 2. La revisione di...
[num_corpo] => Array
(
[0] => a) razionalizzazione e... articolo 1, comma 154, della legge 13 dicembre 2010, n. 220 (Legge di stabilità 2011);
[1] => b) applicazione dei... articolo 119 della Costituzione , nonché del principio...
[2] => c) valorizzazione...
[3] => d) previsione di...
[4] => e) valorizzazione del...
[5] => f) previsione di...
)
...
И это уже с некоторой оптимизацией (вы можете игнорировать неправильные символы в кодировке, это ошибка копирования и вставки).
Возможно, вы захотите использовать библиотеку, которая упрощает запрос дочерних узлов напрямую через xpath. Я дал ответ, который показывает небольшой и быстрый пример использования DOMXPath и функции запроса php, вам нужно добавить поддержку пространства имен для этого. Затем вам нужно будет позаботиться о построении массива, которое на самом деле довольно сложно:
$doc = new DOMDocument();
$doc->load('example.xml');
/* DOMBLAZE II XMLNS */ $doc->registerNodeClass("DOMElement", "DOMBLAZE"); # ...
/** @var $root DOMBLAZE */
$root = $doc->documentElement;
$root()->registerNamespace('a', 'http://www.normeinrete.it/nir/2.1/');
$array = [];
foreach ($root('a:LeggeRegionale') as $leggioRegionale) {
$entry = [];
$entry['intestazione'] = $leggioRegionale('string(./a:intestazione)');
$articolato = [];
foreach ($leggioRegionale('a:articolato/a:articolo') as $articolo) {
}
$array[] = $entry;
}
print_r($array);
Этот пример (очевидно) неполный.
В качестве альтернативы я экспериментировал с записью выражений xpath в самом XML, который определяет "массив". Затем это можно было бы использовать с адаптированным SimpleXMLElement для рекурсивного построения массива на основе определения:
$doc = new DOMDocument();
$doc->load('example.xml');
$buffer = <<<XML
<xmlarray>
<xml>
<namespace prefix="a" uri="http://www.normeinrete.it/nir/2.1/"/>
</xml>
<array>
<LeggeRegionale expr="a:LeggeRegionale">
<intestazione expr="string(a:intestazione)"/>
<articolato expr="a:articolato">
<articolo expr="a:articolo">
<num expr="string(a:num)"/>
<rubrica expr="string(a:rubrica)"/>
<commi expr="a:comma">
<num_alinea expr="concat(a:num, a:alinea)"/>
<el expr="a:el" alias="num_corpo">
<num_corpo expr="normalize-space(concat(a:num, a:corpo))" cast="string"/>
</el>
</commi>
</articolo>
</articolato>
</LeggeRegionale>
</array>
</xmlarray>
XML;
$xmlArray = new XmlArrayElement($buffer);
$xmlArray->assignDocument($doc);
print_r($xmlArray->toArray());
Это действительно создает массив, представленный в начале ответа.
Конечно, теперь это выглядит как супер-решение для вас, но все, что он сделал, это обернул дерево XML в другое дерево, на этот раз в массив. Элемент XmlArrayElement не является частью примера в ответе, но представляет собой суть.
Возможно, лучше использовать рекурсию для создания другого XML-документа на лету.
Также стоит рассмотреть в вашем случае XSLT, который технически был сделан для этого. Вы можете напрямую конвертировать в HTML-документ. Документы HTML гораздо более переносимы, чем документы RTF, и существуют инструменты для их преобразования в RTF, а также другие документы.