разобрать неверный XML вручную

У меня есть недопустимый XML, есть много проблем в самом файле, и я нужно делать ежедневные реимпорты из этого файла. Структура выглядит так:

<products>
<product no="AP1222-00" name="Colours kravata" price="456" currency="Kč">
<description name="POPIS PRODUKTU">Kravata Premier Line v moderních barvách. Materiál polyester. Baleno v sáčku s černým poutkem.</description>
</product>
<product no="AP1222-22" name="Colours kravata" price="330" currency="Kč">
<description name="POPIS PRODUKTU">Blabla.</description>
</product>
</products>

Есть ли простой способ получить массив продуктов, чтобы я мог решить проблемы в он файлы перед импортом? SimpleXML и т.д. не работают, так как файл недействителен.

Редактировать:
Вот один полный продукт XML для справки, обратите внимание на двойные кавычки в названии продукта:

<products>
<product no="AP1222-00" name="" Colours" kravata" price="456" currency="Kč">
<folders>
<folder category="<b>COOL 2017</b>" subcategory="TEXTILE & FASHION"/>
<folder category="TEXTILE & FASHION" subcategory="Kravaty a šály"/>
</folders>
<description name="POPIS PRODUKTU">Kravata Premier Line v moderních barvách. Materiál polyester. Baleno v sáčku s
černým poutkem.
</description>
<properties>
<property name="KS / KARTON" value="100"/>
<property name="HMOTNOST KARTONU" value="6"/>
<property name="NETTO HMOTNOST / KARTON" value="5"/>
<property name="DIM1" value="15"/>
<property name="DIM2" value="80"/>
<property name="DIM3" value="35"/>
<property name="TECHNOLIGIE POTISKU" value="T1 (8C, 50×80 MM)"/>
<property name="TARIF" value="6215200090"/>
<property name="Min. mn. (ks)" value=""/>
<property name="M3/CARTON" value="0.042"/>
<property name="COOL 2017 KAPITOLA" value="TEXTILE AND FASHION"/>
<property name="COOL 2017 STRANY" value="525"/>
<property name="main category" value="fashion"/>
</properties>
<images>
<image src="http://www.andapresent.com/kepek/cms/original/83653.jpg"/>
</images>
<stocks>
<stock name="navi_central" value="2"/>
<stock name="navi_arrive" value="" date=""/>
<stock name="eu_central" value="" date=""/>
<stock name="eu_arrive_1" value="" date=""/>
<stock name="eu_arive_2" value="" date=""/>
</stocks>
</product>
</products>

0

Решение

DOMDocument::loadHTML Метод более снисходительный, чем анализатор XML, и способен автоматически исправлять многие ошибки. Проблема в том, что вы не можете контролировать, как libxml исправит эти ошибки.

Вот почему я предлагаю другой подход с DOMDocument::loadXML (который использует синтаксический анализатор XML), но на этот раз я постараюсь исправить ошибки с помощью пользовательских правил (это не универсальные исправления, но адаптированные к конкретной ситуации)

Когда вы переключаетесь libxml_use_internal_errors() в trueвсе ошибки XML хранятся в массиве libXMLErr экземпляров. Каждый из них содержит код ошибки, строку ошибки и столбец ошибки. (Обратите внимание, что первая строка и первый столбец 1).

$xml = file_get_contents('file.xml');

$dom = new DOMDocument;
libxml_use_internal_errors(true);
$dom->loadXML($xml);
$errors = libxml_get_errors();

if ($errors) {
// LIBXML constant name, LIBXML error code // LIBXML error message
define('XML_ERR_LT_IN_ATTRIBUTE', 38); // Unescaped '<' not allowed in attributes values
define('XML_ERR_ATTRIBUTE_WITHOUT_VALUE', 41); // Specification mandate value for attribute
define('XML_ERR_NAME_REQUIRED', 68); // xmlParseEntityRef: no name

$rules = [
XML_ERR_LT_IN_ATTRIBUTE => [
'pattern' => '~(?:(?!\A)|.{%d}")[^<"]*\K<~A',
'replacement' => [ 'string' => '&lt;', 'size' => 3 ]
],
XML_ERR_ATTRIBUTE_WITHOUT_VALUE => [
'pattern' => '~^.{%d}\h+\w+\h*=\h*"[^"]*\K"([^"]*)"~',
'replacement' => [ 'string' => '&quot;$1&quot;', 'size' => 10 ]
],
XML_ERR_NAME_REQUIRED => [
'pattern' => '~^.{%d}[^&]*\K&~',
'replacement' => [ 'string' => '&amp;', 'size' => 4 ]
]
];

$previousLineNo = 0;
$lines = explode("\n", $xml);

foreach ($errors as $error) {

if (!isset($rules[$error->code])) continue;

$currentLineNo = $error->line;

if ( $currentLineNo != $previousLineNo )
$offset = -1;

$currentLine = &$lines[$currentLineNo - 1];
$pattern = sprintf($rules[$error->code]['pattern'], $error->column + $offset);
$currentLine = preg_replace($pattern,
$rules[$error->code]['replacement']['string'],
$currentLine, -1, $count);
$offset += $rules[$error->code]['replacement']['size'] * $count;
$previousLineNo = $currentLineNo;
}

$xml = implode("\n", $lines);

libxml_clear_errors();
$dom->loadXML($xml);
$errors = libxml_get_errors();
}

var_dump($errors);

$s = simplexml_import_dom($dom);

echo $s->product[0]["name"];

size в массиве правил есть разница между размером замещающей строки и размером заменяемой строки. Таким образом, когда в одной строке несколько ошибок, позиция следующей ошибки обновляется $offset,

Константы ошибок libxml недоступны в PHP, поэтому они определяются вручную (только для того, чтобы сделать код более читабельным). Вы можете найти их Вот.

3

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector