【问题标题】:Convert XML to a PHP array results in losing attribute data after conversion将 XML 转换为 PHP 数组会导致转换后丢失属性数据
【发布时间】:2018-04-19 13:26:20
【问题描述】:

我有一个基于这个函数的方法: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。我根本不明白为什么我的更改版本会发生这种情况,而不是原始代码。

【问题讨论】:

    标签: php arrays xml multidimensional-array


    【解决方案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)) {
    

    【讨论】:

    • 原代码基本上是直接将值存储在键上,但是我需要它们在value里面,所以我像上面一样切换了检查。我知道我的整个修改版本肯定有问题,但我看不出到底是什么。我会测试你的解决方案。
    • 主要区别似乎是,如果有一些子元素,您只会创建数组的 value 元素。在&lt;xs:maxLength value="40"/&gt; 的情况下,没有子元素。
    • 这样合适还是需要改变?
    • 您可以创建它以保持一致性,但恕我直言,您的代码在这种情况下不创建它是正确的。叶节点没有“价值”,因此放入一个可能会导致假设那里应该有一些东西。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-25
    • 2015-09-03
    • 2016-10-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多