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

时间:2018-04-19 13:26:21

标签: php arrays xml multidimensional-array

我有一个基于此功能的方法:https://github.com/gaarf/XML-string-to-PHP-array/blob/master/xmlstr_to_array.php

现在我根据自己的需要对其进行了修改,现在看起来像这样:

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;
}

使用示例XML / XSD字符串并尝试使用上述方法(parseXML)将其转换为数组,将导致某些属性丢失,但仅我的修改版本,它可以使用github存储库中提供的方法正常工作。

示例XSD字符串如下所示:

$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);

此数组的输出将如此(print_r):https://pastebin.com/sYvf5Z4X(使用URL,因为它将超出字符数限制)。

为方便起见,maxLength标记在其出现的所有内容中都丢失了值value的属性40。我根本无法理解为什么我的修改版本会发生这种情况,而不是原始代码。

1 个答案:

答案 0 :(得分:1)

问题是(必须承认我并不完全理解代码的细节)......

这里的代码......

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

仅当为节点设置了值时才有效。我认为会发生的是,任何叶节点都没有值,因此会跳过属性值。

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

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

如果您移动对此分支之外的属性的检查,则可以正常工作。

不同之处在于原始代码没有检查是否存在值集,它只检查那里有什么东西(原始代码的第48行)......

if(is_array($output)) {