Php 手动分析无效的XML

Php 手动分析无效的XML,php,xml,parsing,Php,Xml,Parsing,我…XML…无效…无效…无效,文件本身有很多问题,及我每天从…每天从…每天从…每天从…每天从…每天从…每天从…每天从…每天从…每天从…每天从…每天从…每天从…每天从…每天从…每天从文件结构如下所示: <products> <product no="AP1222-00" name="Colours kravata" price="456" currency="Kč"> <description name="POPIS PRODUKTU">

我…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>

DOMDocument::loadHTML
方法比XML解析器更宽松,能够自动修复许多错误。问题是您无法控制libxml将如何修复这些错误

这就是为什么我建议使用
DOMDocument::loadXML
(使用XML解析器)的另一种方法,但这次我将尝试使用自定义规则纠正错误(这些规则不是通用的修复方法,但可以根据具体情况进行调整)

当您将
libxml\u use\u internal\u errors()
切换到
true
时,所有xml错误都存储在
libXMLErr
实例数组中。它们中的每一个都包含一个错误代码、错误行和错误列。(请注意,第一行和第一列为1)

$xml=file\u get\u contents('file.xml');
$dom=新的DOMDocument;
libxml\u使用\u内部错误(true);
$dom->loadXML($xml);
$errors=libxml_get_errors();
如果($errors){
//LIBXML常量名称,LIBXML错误代码//LIBXML错误消息

define('XML_ERR_LT_IN_ATTRIBUTE',38);//Unescaped'
DOMDocument::loadHTML
方法比XML解析器更宽松,能够自动修复许多错误。问题是您无法控制libxml将如何修复这些错误

这就是为什么我建议使用
DOMDocument::loadXML
(使用XML解析器)的另一种方法,但这次我将尝试使用自定义规则纠正错误(这些规则不是通用的修复方法,但可以根据具体情况进行调整)

libxml\u use\u internal\u errors()
切换到
true
时,所有xml错误都存储在
libXMLErr
实例数组中。每个实例都包含一个错误代码、错误行和错误列(请注意,第一行和第一列为1)

$xml=file\u get\u contents('file.xml');
$dom=新的DOMDocument;
libxml\u使用\u内部错误(true);
$dom->loadXML($xml);
$errors=libxml_get_errors();
如果($errors){
//LIBXML常量名称,LIBXML错误代码//LIBXML错误消息

define('XML_ERR_LT_IN_ATTRIBUTE',38);//Unescaped'我看不出上述XML有任何无效的地方吗?我只是以该结构为例发布了一篇文章,还有很多行的内容类似于
name=”“Something“other”
等。如果文件包含错误,请尝试将其解析为html文件(
DOMDocument::loadHTML
)请准确描述它是如何无效的。你不能修复空的XML,你不能修复扩展名重命名为
.png
的纯图像XML…你真的应该发布无效XML的示例。如果我们甚至不知道错误是什么,很难回答这个问题。但我认为你将很难编写解析器,特别是如果文件不一致,则使用regex。这通常是regex的不足之处。因此,您的问题的答案很可能是:不,没有简单的方法。我没有发现上面的XML有任何无效之处?我只是将结构作为一个示例发布,还有许多行包含类似
name=”“Something“other”的内容
etc。如果文件包含错误,请尝试将其解析为html文件(
DOMDocument::loadHTML
)请准确描述它是如何无效的。你不能修复空的XML,你不能修复扩展名重命名为
.png
的纯图像XML…你真的应该发布无效XML的示例。如果我们甚至不知道错误是什么,很难回答这个问题。但我认为你将很难编写解析器,特别是如果文件不一致,则使用regex。这通常是regex的不足之处。因此,您的问题的答案很可能是:不,没有简单的方法。没有理由任何人会对我在中引用的这一优秀答案投反对票。没有理由任何人会对我在中引用的这一优秀答案投反对票。
<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>
$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"];