将XML转换为PHP数组会导致转换后丢失属性数据

I have a method which is based on this function: https://github.com/gaarf/XML-string-to-PHP-array/blob/master/xmlstr_to_array.php

Now I altered it to suit my needs, which looks like this now:

private function parseXml($xmlString)
{
    $doc = new \DOMDocument;
    $doc->loadXML($xmlString);
    $root = $doc->documentElement;
    $output[$root->tagName] = $this->domnodeToArray($root);

    return $output;
}

/**
 * @param $node
 * @return array|string
 */
private function domNodeToArray($node)
{
    $output = [];
    switch ($node->nodeType)
    {
        case XML_CDATA_SECTION_NODE:
        case XML_TEXT_NODE:
            $output = trim($node->textContent);
            break;
        case XML_ELEMENT_NODE:
            for ($i = 0, $m = $node->childNodes->length; $i < $m; $i++)
            {
                $child = $node->childNodes->item($i);
                $v = $this->domNodeToArray($child);

                if (isset($child->tagName))
                {
                    $t = $child->tagName;

                    if (!isset($output['value'][$t]))
                    {
                        $output['value'][$t] = [];
                    }

                    $output['value'][$t][] = $v;
                }
                else if ($v || $v === '0')
                {
                    $output['value'] = (string)$v;
                }
            }

            if (isset($output['value']) && $node->attributes->length && !is_array($output['value']))
            {
                $output = ['value' => $output['value']];
            }

            if (!$node->attributes->length && isset($output['value']) && !is_array($output['value']))
            {
                $output = ['attributes' => [], 'value' => $output['value']];
            }

            if (isset($output['value']) && is_array($output['value']))
            {
                if ($node->attributes->length)
                {
                    $a = [];
                    foreach ($node->attributes as $attrName => $attrNode)
                    {
                        $a[$attrName] = (string)$attrNode->value;
                    }
                    $output['attributes'] = $a;
                }
                else
                {
                    $output['attributes'] = [];
                }

                foreach ($output['value'] as $t => $v)
                {
                    if (is_array($v) && count($v) == 1 && $t != 'attributes')
                    {
                        $output['value'][$t] = $v[0];
                    }
                }
            }
            break;
    }

    return $output;
}

Taking an example XML/XSD string and trying to convert it to an array with the method above (parseXML), will result in the loss of some attributes, but only when using my altered version, it works properly with the methods provided in the github repository.

The example XSD string looks like so:

$xsdStr = '<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xs:element name="book">
        <xs:complexType>

            <xs:sequence>
                <xs:element name="title">
                    <xs:simpleType>
                        <xs:restriction base="xs:string">
                            <xs:maxLength value="40"/>
                        </xs:restriction>
                    </xs:simpleType>
                </xs:element>

                <xs:element name="author">
                    <xs:simpleType>
                        <xs:restriction base="xs:string">
                            <xs:maxLength value="40"/>
                        </xs:restriction>
                    </xs:simpleType>
                </xs:element>

                <xs:element name="character" maxOccurs="unbounded" minOccurs="0">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="name">
                                <xs:simpleType>
                                    <xs:restriction base="xs:string">
                                        <xs:maxLength value="40"/>
                                    </xs:restriction>
                                </xs:simpleType>
                            </xs:element>
                            <xs:element name="friend-of" maxOccurs="unbounded" minOccurs="0">
                                <xs:simpleType>
                                    <xs:restriction base="xs:string">
                                        <xs:maxLength value="40"/>
                                    </xs:restriction>
                                </xs:simpleType>
                            </xs:element>
                            <xs:element name="since" type="xs:date"/>
                            <xs:element name="qualification" type="xs:string"/>
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
            <xs:attribute name="isbn" use="required"> 
                <xs:simpleType>
                    <xs:restriction base="xs:integer">
                        <xs:totalDigits value="10"/>
                    </xs:restriction>
                </xs:simpleType>
            </xs:attribute> 

        </xs:complexType>
    </xs:element>

</xs:schema>';

echo '<pre>';
echo print_r($this->parseXml($xsdStr), true);

The output of this array will looks like so (print_r): https://pastebin.com/sYvf5Z4X (using URL as it will exceed the character limit).

To make it easier, the maxLength tag loses its attribute value with the value 40 in all occurrences of it. I simply can't see why that is happening with my altered version, but not the original code.

1个回答



问题是(必须承认我并不完全理解代码的细节)...... </ p> \ n

这里的代码... </ p>

  if(isset($ output ['value'])&amp;&amp; is_array($ output ['value  ']))
{
if if($ node-&gt; attributes-&gt; length)
</ code> </ pre>

这仅适用于为此设置的值 节点。 我认为会发生的是,任何叶节点都没有值,因此会跳过属性值。</ p>

  if($ node-&gt; attributes-&gt; length)

{
// ...
}

if(isset($ output ['value'])&amp;&amp; is_array($ output ['value']))
</ code> < / pre>

如果你移动检查这个分支之外的属性它可以正常工作。</ p>

区别在于原始代码没有检查是否存在 值集,它只是检查那里有东西(原始代码的第48行)...... </ p>

  if(is_array($ output)){
</ code> </ pre>
</ div>

展开原文

原文

The problem is (must admit I don't totally understand the ins and outs of the code)...

In this code here...

if (isset($output['value']) && is_array($output['value']))
{
     if ($node->attributes->length)

This only works if there is a value set for the node. I think what happens is that any leaf node doesn't have a value and therefore the attribute values are skipped.

if ($node->attributes->length)
{
            // ...
}

if (isset($output['value']) && is_array($output['value']))

If you move the check for attributes outside of this branch it works OK.

The difference is that the original code doesn't check there is a value set, it just checks that there is something there (line 48 from original code) ...

if(is_array($output)) {

duanbi9202
duanbi9202 您可以创建它以保持一致性,但恕我直言,您的代码在这种情况下不创建它是正确的。 叶子节点没有“价值”,因此放入一个可能会导致假设应该存在某些东西。
2 年多之前 回复
doufencigui933699
啊啊啊小孔 这是合适的还是需要改变?
2 年多之前 回复
douyan1972
douyan1972 主要的区别似乎是你只创建了数组的value元素,如果有一些子元素。 在<xs:maxLength value =“40”/>的情况下,没有子元素。
2 年多之前 回复
duancashi1362
duancashi1362 原始代码基本上将值直接存储在键上,但我需要它们在内部值,所以我像上面那样切换了检查。 我知道我的修改版本总体上有一些问题,但我看不清究竟是什么。 我会测试你的解决方案。
2 年多之前 回复
立即提问
相关内容推荐