Я использую XMLReader и PHP для обработки XML-файла умеренного размера (6 МБ) и в основном разбиваю данные атрибута и вставляю его в свою собственную базу данных. Проблема в том, что каждый элемент имеет переменное количество подэлементов с одинаково названными атрибутами.
Вот пример (это открытые данные о государственной вежливости govtrack.us):
<?xml version="1.0" ?> <people> <person id='400001' lastname='Abercrombie' firstname='Neil' birthday='1938-06-26' gender='M' pvsid='26827' osid='N00007665' bioguideid='A000014' metavidid='Neil_Abercrombie' youtubeid='hawaiirep1' name='Rep. Neil Abercrombie [D, HI-1]' title='Rep.' state='HI' district='1' > <role type='rep' startdate='1985-01-03' enddate='1986-10-18' party='Democrat' state='HI' district='1' /> <role type='rep' startdate='1991-01-03' enddate='1992-10-09' party='Democrat' state='HI' district='1' /> <role type='rep' startdate='1993-01-05' enddate='1994-12-01' party='Democrat' state='HI' district='1' /> <role type='rep' startdate='1995-01-04' enddate='1996-10-04' party='Democrat' state='HI' district='1' /> <role type='rep' startdate='1997-01-07' enddate='1998-12-19' party='Democrat' state='HI' district='1' /> <role type='rep' startdate='1999-01-06' enddate='2000-12-15' party='Democrat' state='HI' district='1' /> <role type='rep' startdate='2001-01-03' enddate='2002-11-22' party='Democrat' state='HI' district='1' /> <role type='rep' startdate='2003-01-07' enddate='2004-12-09' party='Democrat' state='HI' district='1' url='http://www.house.gov/abercrombie' /> <role type='rep' startdate='2005-01-04' enddate='2006-12-08' party='Democrat' state='HI' district='1' url='http://www.house.gov/abercrombie' /> <role type='rep' startdate='2007-01-04' enddate='2009-01-03' party='Democrat' state='HI' district='1' url='http://www.house.gov/abercrombie' /> <role type='rep' startdate='2009-01-06' enddate='2010-03-01' party='Democrat' state='HI' district='1' url='http://www.house.gov/abercrombie' /> </person>
Мне не нужна какая-то фантастическая логика для атрибутов. В начале моего сценария я проверяю, не обработал ли я эту конкретную запись (на основе атрибута id), а затем я хватаю почти каждый атрибут и анализирую его в своем db. Но есть две проблемы:
1) Когда я использую это:
$p->getAttribute('id')
чтобы получить «id», он дает его мне дважды, разделяется на столько разрывов строк, что есть элементы в элементе (я думаю, комментарий на этой странице говорит об этом, но я не уверен, что с этим делать ).
2) Как я могу получить доступ к атрибутам каждого подэлемента последовательно? Эта:
$p->getAttribute('startdate')
дает мне каждое значение «startdate», разделенное несколькими разрывами строк. Мне просто нужно захватить идентификатор элемента, а затем прокрутить каждый из подэлементов «role».
Есть идеи?
Для назидания, вот супер-простой контроллер, который у меня есть до сих пор:
$f = base_url().'data/people.xml'; $p = new XMLReader; $p->open($f); while($p->read()) { if($this->_notImported('govtrack',$p->getAttribute('id'))) { // here I just grab the attributes, put them into arrays to insert, like so: $insert = array('indiv_name' => $full_name, 'indiv_first' => ($p->getAttribute('firstname')), 'indiv_last' => ($p->getAttribute('lastname')), 'indiv_middle' => ($p->getAttribute('middlename')), 'indiv_other' => ($p->getAttribute('namemod')), 'indiv_full_name' => $full_name, 'indiv_title' => ($p->getAttribute('title')), 'indiv_dob' => ($p->getAttribute('birthday')), 'indiv_gender' => ($p->getAttribute('gender')), 'indiv_religion' => ($p->getAttribute('religion')), 'indiv_url' => ($url) );
в$f = base_url().'data/people.xml'; $p = new XMLReader; $p->open($f); while($p->read()) { if($this->_notImported('govtrack',$p->getAttribute('id'))) { // here I just grab the attributes, put them into arrays to insert, like so: $insert = array('indiv_name' => $full_name, 'indiv_first' => ($p->getAttribute('firstname')), 'indiv_last' => ($p->getAttribute('lastname')), 'indiv_middle' => ($p->getAttribute('middlename')), 'indiv_other' => ($p->getAttribute('namemod')), 'indiv_full_name' => $full_name, 'indiv_title' => ($p->getAttribute('title')), 'indiv_dob' => ($p->getAttribute('birthday')), 'indiv_gender' => ($p->getAttribute('gender')), 'indiv_religion' => ($p->getAttribute('religion')), 'indiv_url' => ($url) );
Для элемента это не так сложно, но я не знаю, как циклически перебирать каждый из подэлементов «роли» и захватывать атрибуты отдельно.
Ваша первая проблема заключается в том, что вы не проверяете соответствующий тип nodeType , который на самом деле связан с комментарием, который вы связали: он соответствует как для открывающего тега (ELEMENT), так и закрывающего тега (END_ELEMENT).
Вторая проблема связана также с отсутствием проверки узла nodeType . После того, как вы исправите это, вам просто нужно проверить имя узла, чтобы узнать, является ли это <role>
или <person>
.
Поскольку я предполагаю, что вы также читаете большой XML-файл, вы, вероятно, захотите узнать, когда вы переходите к тегу следующего человека … (через END_ELEMENT nodeType ) См. Мой пример ниже:
while($p->read()) { // check for nodeType here (opening tag only) if ($p->nodeType == XMLReader::ELEMENT) { if ($p->name == 'person') { if ($this->_notImported('govtrack',$p->getAttribute('id'))) { // $insert['indiv_*'] stuff here } else { $insert = null; // skip record because it's already imported } } else if ($p->name == 'role') { // role stuff here $startdate = $p->getAttribute('startdate'); } // check for closing </person> tag here } else if ($p->nodeType == XMLReader::END_ELEMENT && $p->name == 'person') { if (isset($insert)) { // db insert here } } }
Кстати, ваши кавычки должны быть заменены соответствующими кавычками '
если вы хотите, чтобы это работало.