Как разобрать сложный xml

Мне кажется это из за xmlns="goods-schema.dtd", оно описывает xml документ, по этому атрибуты могут пропадать. Где этот файл можно скачать? В интернете чтото не нашёл. В самом 1ц он есть?
Вот содержимое этого файла, правда зовется он goods-schema.xsd
Код:
<xs:schema xmlns:tns="goods-schema.dtd" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="goods-schema.dtd" attributeFormDefault="qualified" elementFormDefault="unqualified">
    <xs:element name="Товар" type="tns:Товар"/>
    <xs:element name="Товары" type="tns:Товары"/>
    <xs:element name="ДанныеОбмена" type="tns:ДанныеОбмена"/>
    <xs:complexType name="ДанныеОбмена">
        <xs:sequence>
            <xs:element ref="tns:Товары"/>
        </xs:sequence>
        <xs:attribute name="ИБ" type="xs:string"/>
        <xs:attribute name="Дата" type="xs:string"/>
        <xs:attribute name="Пользователь" type="xs:string"/>
    </xs:complexType>
    <xs:complexType name="Товар">
        <xs:sequence>
            <xs:element name="price0" type="tns:Цены"/>
            <xs:element name="price1" type="tns:Цены"/>
            <xs:element name="price2" type="tns:Цены"/>
            <xs:element name="price3" type="tns:Цены"/>
            <xs:element name="price_d2" type="tns:Цены"/>
            <xs:element name="price4" type="tns:Цены"/>
            <xs:element name="price5" type="tns:Цены"/>
            <xs:element name="pricesimf" type="tns:Цены"/>
            <xs:element name="price_dbn" type="tns:Цены"/>
        </xs:sequence>
        <xs:attribute name="id" type="xs:string" use="required"/>
        <xs:attribute name="Code" type="xs:string" use="required"/>
        <xs:attribute name="EAN" type="xs:string" use="required"/>
        <xs:attribute name="tovar" type="xs:string" use="required"/>
        <xs:attribute name="vendor" type="xs:string" use="required"/>
        <xs:attribute name="edname" type="xs:string" use="required"/>
        <xs:attribute name="parent_code" type="xs:string" use="required"/>
        <xs:attribute name="reserv_sev" type="xs:string" use="required"/>
        <xs:attribute name="reserv_simf" type="xs:string" use="required"/>
        <xs:attribute name="reserv_yalta" type="xs:string" use="required"/>
        <xs:attribute name="reserv_kacha" type="xs:string" use="required"/>
        <xs:attribute name="quantity_sev" type="xs:string" use="required"/>
        <xs:attribute name="quantity_simf" type="xs:string" use="required"/>
        <xs:attribute name="quantity_yalta" type="xs:string" use="required"/>
        <xs:attribute name="quantity_kacha" type="xs:string" use="required"/>
        <xs:attribute name="pn" type="xs:string" use="required"/>
        <xs:attribute name="warranty" type="xs:string" use="required"/>
    </xs:complexType>
    <xs:complexType name="Товары">
        <xs:sequence>
            <xs:element ref="tns:Товар" maxOccurs="unbounded"/>
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="Цены">
        <xs:attribute name="Цена" type="xs:string" use="required"/>
        <xs:attribute name="Валюта" type="xs:string" use="required"/>
    </xs:complexType>
</xs:schema>
 
Вот содержимое этого файла, правда зовется он goods-schema.xsd

По идее оно, но я хз как надо пихать его в парсер, постоянно ругается на ошибки. Пробуй без него -
Код:
$xmlcode = <<<XML
<?xml version="1.0" encoding="UTF-8"?>       
<ДанныеОбмена xmlns="goods-schema.dtd" xmlns:d1p1="goods-schema.dtd" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" d1p1:ИБ="УНФ" d1p1:Дата="2017-05-04T14:10:05" d1p1:Пользователь="Пользоватль">
  <Товары>
  <Товар xmlns:d3p1="goods-schema.dtd" d3p1:id="c1449a62-7346-11e6-a99b-60e32702ecc8" d3p1:Code="SN00387" d3p1:EAN="_" d3p1:tovar="_rechar bat GP AA NM 2700 mAh 1x2 шт." d3p1:vendor="GP" d3p1:edname="шт" d3p1:parent_code="ФР-00000177" d3p1:reserv_sev="0" d3p1:reserv_simf="0" d3p1:reserv_yalta="0" d3p1:reserv_kacha="0" d3p1:quantity_sev="1" d3p1:quantity_simf="0" d3p1:quantity_yalta="0" d3p1:quantity_kacha="0" d3p1:pn="270AAHC-BL2" d3p1:warranty="0">
  <price0 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price1 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price2 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price3 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price_d2 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price4 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price5 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <pricesimf xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price_dbn xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  </Товар>
  <Товар xmlns:d3p1="goods-schema.dtd" d3p1:id="23b0c4d1-3dfa-11e6-9a4d-60e32702ecc8" d3p1:Code="SN29387" d3p1:EAN="_" d3p1:tovar="_Стяжка нейлоновая 100x2.5, 100 шт Telecom (CV-100)" d3p1:vendor="" d3p1:edname="шт" d3p1:parent_code="ФР-00000194" d3p1:reserv_sev="0" d3p1:reserv_simf="0" d3p1:reserv_yalta="0" d3p1:reserv_kacha="0" d3p1:quantity_sev="520" d3p1:quantity_simf="0" d3p1:quantity_yalta="0" d3p1:quantity_kacha="0" d3p1:pn="" d3p1:warranty="0">
  <price0 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price1 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price2 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price3 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price_d2 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price4 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price5 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <pricesimf xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price_dbn xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  </Товар>
  <Товар xmlns:d3p1="goods-schema.dtd" d3p1:id="23b0c4d2-3dfa-11e6-9a4d-60e32702ecc8" d3p1:Code="SN39109" d3p1:EAN="_" d3p1:tovar="_Стяжка нейлоновая 150x2.5, 100 шт Telecom" d3p1:vendor="" d3p1:edname="шт" d3p1:parent_code="ФР-00000194" d3p1:reserv_sev="0" d3p1:reserv_simf="0" d3p1:reserv_yalta="0" d3p1:reserv_kacha="0" d3p1:quantity_sev="32" d3p1:quantity_simf="0" d3p1:quantity_yalta="0" d3p1:quantity_kacha="0" d3p1:pn="" d3p1:warranty="0">
  <price0 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price1 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price2 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price3 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price_d2 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price4 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price5 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <pricesimf xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price_dbn xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  </Товар>
  <Товар xmlns:d3p1="goods-schema.dtd" d3p1:id="23b0c4d3-3dfa-11e6-9a4d-60e32702ecc8" d3p1:Code="SN36046" d3p1:EAN="_" d3p1:tovar="_Стяжка нейлоновая 200x2.5, 100 шт Telecom" d3p1:vendor="" d3p1:edname="шт" d3p1:parent_code="ФР-00000194" d3p1:reserv_sev="0" d3p1:reserv_simf="0" d3p1:reserv_yalta="0" d3p1:reserv_kacha="0" d3p1:quantity_sev="209" d3p1:quantity_simf="0" d3p1:quantity_yalta="0" d3p1:quantity_kacha="0" d3p1:pn="" d3p1:warranty="0">
  <price0 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price1 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price2 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price3 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price_d2 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price4 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price5 xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <pricesimf xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  <price_dbn xmlns="" d3p1:Цена="0" d3p1:Валюта="643" />
  </Товар>
</Товары>
</ДанныеОбмена>
XML;
$xmlcode_fix = $xmlcode;
$xmlcode_fix = str_replace('xmlns:d3p1="goods-schema.dtd"', '', $xmlcode_fix);
$xmlcode_fix = str_replace('xmlns="goods-schema.dtd"', '', $xmlcode_fix);
$xmlcode_fix = str_replace('xmlns:d1p1="goods-schema.dtd"', '', $xmlcode_fix);


$s =    @simplexml_load_string($xmlcode_fix, 'SimpleXMLElement',  LIBXML_NOWARNING);


foreach ($s->Товары->Товар as $offer) {
 
    var_dump((string)$offer['d3p1:Code'], (string) $offer['d3p1:tovar'], (string) $offer->price1['d3p1:Цена']);
}
 
Все что можно и нужно по парсингу проще и лучше смотреть из оф.документации + в комментах бывают лулзы полезные - Для просмотра ссылки Войди или Зарегистрируйся
 
Парси xml стандартной библиотекой xmlreader. Принцип простой, выбираешь более или менее небольшой повторяющийся кусок. У тебя это:
<товар>
...
<товар>

Вот образец моей парсилки yandex market xml.
Метода такая, сначала проходит весь xml, чтобы составить каркас таблицы в которую все это будет писать, т.е. определяется количество столбцов, которые у нас будут и их названия. На втором проходе уже записываются значения таблицы.




Код:
...
$z = new XMLReader;
$z->open($filename_xml);
$f = fopen('output.csv', 'w+');
while ($z->read() && $z->name !== 'offer'); { //тут у тебя будет твой товар вместо offer
    $count = 0;
    while ($z->name === 'offer') //тут тоже
    {
        // either one should work
        $node = new SimpleXMLElement($z->readOuterXML()); //тут делаем объект из этого куска через еще 1 стандартную либу


        // создаем массив $a со значениями
        $a = array(); //тут будут значения для их последующего копирования в массив для окончательной записи
        $attrib = array();      //тут будут итоговые значения для окончательный записи в файл
        foreach($node as $k=>$n) { //у меня парсит все аттрибуты тега в 1 строку через запятую <param name='длина' units='мм'> в 'длина, мм'
            if ($n->attributes()) {
                $nnn_name = array();
                foreach ($n->attributes() as $nnn)
                $nnn_name[] = $nnn;
                $a[$k.'_'.implode(',', $nnn_name)] = (string)$n;
            } else {
                $a[$k] = (string)$n ;
            }
        }

    
        foreach($fields_tpl as $kk=>$nn) {
            if(isset($a[$kk])) {
            $attrib[$kk] = $a[$kk];
            } else {
                $attrib[$kk] = ''; //если в этом товаре такого параметра нет, то делаем пустой элемент
            }
        }
        
        // добавляем в массив offer_id
        $offer_id = $node->attributes();
        $offer_id = $offer_id['id'];
        $attrib['offer_id'] = (string)$offer_id;
        

        //пишем в файл
        fputcsv($f, $attrib);
                  
        // go to next <product />
        $z->next('offer');


        $count++;
    }
}
fclose($f);

Жуткая жесть - открыли файл, записал пару символов или строку, закрыли файл. 3 раза подряд! И это всё внутри 2х относительно быстрых циклов парсинга :work:
Операции IO одни из самых дорогих, особенно на шаред хостингах.
Если переписать это нормально, то работать будет в 10-100 раз быстрее.

PS при этом, локально на хорошем ssd замедление может быть не заметно.

upd: Спасибо, исправил
 
Последнее редактирование:
PHP:
        // выводим кавычки в начало
        file_put_contents( $filename_csv, '"', FILE_APPEND); 
   
        file_put_contents( $filename_csv, implode( '","' , $attrib),  FILE_APPEND); //пишем массив в строку через запятую
       
        // выводим кавычки в конец
        file_put_contents( $filename_csv, "\"\n", FILE_APPEND);
Жуткая жесть - открыли файл, записал пару символов или строку, закрыли файл. 3 раза подряд! И это всё внутри 2х относительно быстрых циклов парсинга :work:
Операции IO одни из самых дорогих, особенно на шаред хостингах.
Если переписать это нормально, то работать будет в 10-100 раз быстрее.

PS при этом, локально на хорошем ssd замедление может быть не заметно.
 
Для работы с XML очень рекомендую нативную SimpleXML и XSLT
Для просмотра ссылки Войди или Зарегистрируйся

Ниже рабочий код. Он получает xml-файл с помощью curl, создает dom который и обходит складывая данные в массив $DATA. Обходит не весь, а только с узла определенного через xpath.


PHP:
/* Инициализация массива для данных из XML */
$DATA =array();

/* Получаем XML */
$request ='http://api.example.com/and/other/parts/of/url';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $request);
curl_setopt($ch, CURLOPT_HEADER, false);
ob_start(); curl_exec($ch);
if(curl_errno($ch) != 0 ){
   die('CURL_error: ' . curl_errno($ch) . ', ' . curl_error($ch));
};
$xmlFile = ob_get_contents(); ob_end_clean(); curl_close($ch);

/* Парсим XML */
$xmlDoc = new DOMDocument();
$xmlDoc->loadXML($xmlFile);
$sxml = simplexml_import_dom($xmlDoc);
foreach ($sxml->xpath('/EventsSet/Event') as $Event) {

        $tmpData = array();

        $tmpData['eventID'] =(string)$Event->Id;
        $tmpData['eventTitle'] =(string)$Event->Title;
        $tmpData['eventGenreID'] = (string)$Event->Genre->attributes()->id;
        $tmpData['eventGenreTitle'] = (string)$Event->Genre->attributes()->title;

        array_push($DATA, $tmpData);

        unset($tmpData);
}

Фрагмент XML

Код:
<EventsSet>

   <Event>
      <Id>123</Id>
      <Title>Мероприятие</Title>
      <Genre id="123" title="название"></Genre>
   </Event>

   <Event>
      <Id>456</Id>
      <Title>Мероприятие 2</Title>
      <Genre id="456" title="название 2"></Genre>
   </Event>

</EventsSet>
 
Последнее редактирование:
Назад
Сверху