带有 php 或 python 属性的 xml 到 json

Posted

技术标签:

【中文标题】带有 php 或 python 属性的 xml 到 json【英文标题】:xml to json with attributes for php or python 【发布时间】:2015-09-25 04:45:46 【问题描述】:

我正在尝试将一些 XML 转换为 JSON,这对 php 来说很容易

$file = file_get_contents('data.xml' );
$a = json_decode(json_encode((array) simplexml_load_string($file)),1);
print_r($a);

采用以下 XML

<?xml version="1.0" encoding="UTF-8"?>
<foo>
    <bar>
        <one lang="fr" type="bar">Test</one>
        <one lang="fr" type="foo">Test</one>
        <one lang="fr" type="baz">Test</one>
    </bar>

    <thunk>
        <thud>
            <bar lang="fr" name="bob">test</bar>
            <bar lang="bz" name="frank">test</bar>
            <bar lang="ar" name="alive">test</bar>
            <bar lang="fr" name="bob">test</bar>
        </thud>
    </thunk>

</foo>

并通过simplexml生成

Array
(
    [bar] => Array
        (
            [one] => Array
                (
                    [0] => Test
                    [1] => Test
                    [2] => Test
                )

        )

    [thunk] => Array
        (
            [thud] => Array
                (
                    [bar] => Array
                        (
                            [0] => test
                            [1] => test
                            [2] => test
                            [3] => test
                        )

                )

        )

)

理想的输出应该是这样的


    "foo": 
        "bar": 
            "one": [
                
                    "_lang": "fr",
                    "_type": "bar",
                    "__text": "Test"
                ,
                
                    "_lang": "fr",
                    "_type": "foo",
                    "__text": "Test"
                ,
                
                    "_lang": "fr",
                    "_type": "baz",
                    "__text": "Test"
                
            ]
        ,
        "thunk": 
            "thud": 
                "bar": [
                    
                        "_lang": "fr",
                        "_name": "bob",
                        "__text": "test"
                    ,
                    
                        "_lang": "bz",
                        "_name": "frank",
                        "__text": "test"
                    ,
                    
                        "_lang": "ar",
                        "_name": "alive",
                        "__text": "test"
                    ,
                    
                        "_lang": "fr",
                        "_name": "bob",
                        "__text": "test"
                    
                ]
            
        
    

问题是输出不包含子元素的所有属性,其中一些元素包含两个或多个属性,有没有办法用 PHP 或 Python 转换 xml 并包含所有属性孩子们?

谢谢

【问题讨论】:

在您的问题中,您正在解码 JSON,而不是对其进行编码。只是注意到。 【参考方案1】:

在我的回答中,我将介绍 PHP,特别是 SimpleXMLElement,它已经是您代码的一部分。

使用 SimpleXMLElement 对 XML 进行 JSON 编码的基本方法与您的问题类似。您实例化 XML 对象,然后 json_encode 它 (Demo):

$xml = new SimpleXMLElement($buffer);
echo json_encode($xml, JSON_PRETTY_PRINT);

这会产生一个接近的输出,但与您正在寻找的不完全一样。因此,您在这里使用 simplexml 所做的就是更改 json_encode 对 XML 对象进行编码的标准方式。

这可以通过实现 JsonSerializable 接口的 SimpleXMLElement 新子类型来完成。这是一个具有 default 方式的类,PHP 如何对对象进行 JSON 序列化:

class JsonSerializer extends SimpleXmlElement implements JsonSerializable

    /**
     * SimpleXMLElement JSON serialization
     *
     * @return null|string
     *
     * @link http://php.net/JsonSerializable.jsonSerialize
     * @see JsonSerializable::jsonSerialize
     */
    function jsonSerialize()
    
        return (array) $this;
    

使用它将产生完全相同的输出 (Demo):

$xml = new JsonSerializer($buffer);
echo json_encode($xml, JSON_PRETTY_PRINT);

所以现在有趣的部分来改变序列化只是这些位来获得你的输出。

首先,您需要区分它是携带其他元素(有子元素)的元素还是它是您想要其属性和文本值的叶元素:

    if (count($this)) 
        // serialize children if there are children
        ...
     else 
        // serialize attributes and text for a leaf-elements
        foreach ($this->attributes() as $name => $value) 
            $array["_$name"] = (string) $value;
        
        $array["__text"] = (string) $this;
    

使用 if/else 即可。 if-block 用于子元素,else-block 用于叶元素。由于叶子元素更容易,我将它们保留在上面的示例中。正如您在 else-block 中看到的那样,它遍历所有属性并添加它们的名称前缀为“_”,最后通过转换为字符串添加“__text”条目。

子元素的处理有点复杂,因为您需要区分仅具有其名称的单个子元素或具有相同名称的多个子元素(需要在内部添加一个额外的数组):

        // serialize children if there are children
        foreach ($this as $tag => $child) 
            // child is a single-named element -or- child are multiple elements with the same name - needs array
            if (count($child) > 1) 
                $child = [$child->children()->getName() => iterator_to_array($child, false)];
            
            $array[$tag] = $child;
        

现在序列化需要处理另一种特殊情况。您对根元素名称进行编码。因此,该例程需要检查该条件(即所谓的 document-element)(与 SimpleXML Type Cheatsheet 比较)并在某些情况下序列化为该名称:

    if ($this->xpath('/*') == array($this)) 
        // the root element needs to be named
        $array = [$this->getName() => $array];
    

最后剩下要做的就是返回数组:

    return $array;

一起编译,这是一个 JsonSerializer 在 simplexml 中根据您的需求量身定制。这里的类和它的调用一次:

class JsonSerializer extends SimpleXmlElement implements JsonSerializable

    /**
     * SimpleXMLElement JSON serialization
     *
     * @return null|string
     *
     * @link http://php.net/JsonSerializable.jsonSerialize
     * @see JsonSerializable::jsonSerialize
     */
    function jsonSerialize()
    
        if (count($this)) 
            // serialize children if there are children
            foreach ($this as $tag => $child) 
                // child is a single-named element -or- child are multiple elements with the same name - needs array
                if (count($child) > 1) 
                    $child = [$child->children()->getName() => iterator_to_array($child, false)];
                
                $array[$tag] = $child;
            
         else 
            // serialize attributes and text for a leaf-elements
            foreach ($this->attributes() as $name => $value) 
                $array["_$name"] = (string) $value;
            
            $array["__text"] = (string) $this;
        

        if ($this->xpath('/*') == array($this)) 
            // the root element needs to be named
            $array = [$this->getName() => $array];
        

        return $array;
    


$xml = new JsonSerializer($buffer);
echo json_encode($xml, JSON_PRETTY_PRINT);

输出(Demo):


    "foo": 
        "bar": 
            "one": [
                
                    "_lang": "fr",
                    "_type": "bar",
                    "__text": "Test"
                ,
                
                    "_lang": "fr",
                    "_type": "foo",
                    "__text": "Test"
                ,
                
                    "_lang": "fr",
                    "_type": "baz",
                    "__text": "Test"
                
            ]
        ,
        "thunk": 
            "thud": 
                "bar": [
                    
                        "_lang": "fr",
                        "_name": "bob",
                        "__text": "test"
                    ,
                    
                        "_lang": "bz",
                        "_name": "frank",
                        "__text": "test"
                    ,
                    
                        "_lang": "ar",
                        "_name": "alive",
                        "__text": "test"
                    ,
                    
                        "_lang": "fr",
                        "_name": "bob",
                        "__text": "test"
                    
                ]
            
        
    

我希望这会有所帮助。一次可能有点多,您可以找到JsonSerializable interface documented in the PHP manual as well,您可以在那里找到更多示例。 *** 上使用这种 XML 到 JSON 转换的另一个示例可以在这里找到:XML to JSON conversion in PHP SimpleXML。

【讨论】:

【参考方案2】:

我扩展了 hakre 的答案。 现在可以更好地区分多个孩子。包括除根元素之外的整个链的属性。

/**
 * Class JsonSerializer
 */
class JsonSerializer extends SimpleXmlElement implements JsonSerializable

    const ATTRIBUTE_INDEX = "@attr";
    const CONTENT_NAME = "_text";

    /**
     * SimpleXMLElement JSON serialization
     *
     * @return array
     *
     * @link http://php.net/JsonSerializable.jsonSerialize
     * @see JsonSerializable::jsonSerialize
     * @see https://***.com/a/31276221/36175
     */
    function jsonSerialize()
    
        $array = [];

        if ($this->count()) 
            // serialize children if there are children
            /**
             * @var string $tag
             * @var JsonSerializer $child
             */
            foreach ($this as $tag => $child) 
                $temp = $child->jsonSerialize();
                $attributes = [];

                foreach ($child->attributes() as $name => $value) 
                    $attributes["$name"] = (string) $value;
                

                $array[$tag][] = array_merge($temp, [self::ATTRIBUTE_INDEX => $attributes]);
            
         else 
            // serialize attributes and text for a leaf-elements
            $temp = (string) $this;

            // if only contains empty string, it is actually an empty element
            if (trim($temp) !== "") 
                $array[self::CONTENT_NAME] = $temp;
            
        

        if ($this->xpath('/*') == array($this)) 
            // the root element needs to be named
            $array = [$this->getName() => $array];
        

        return $array;
    

【讨论】:

老兄,你是我的英雄。奇迹般有效。谢谢!也非常感谢@hakre!【参考方案3】:

您可以使用the lxml library for python

这是一个强大的工具,可以让您引用元素的属性。

【讨论】:

虽然理论上这可以回答这个问题,it would be preferable 在这里包含答案的基本部分,并提供链接以供参考。

以上是关于带有 php 或 python 属性的 xml 到 json的主要内容,如果未能解决你的问题,请参考以下文章

php怎么生成带冒号的节点和属性的,xml使用SimpleXMLElement类或其他php类

PHP将带有一些(重复)元素的XML转换为Json到Json数组[重复]

带有属性而不是元素的 Json 到 XML

SQL 查询到带有标签属性的 XML

PHP SimpleXML 不保留 XML 属性中的换行符

将 Action<T> 回调指定为带有 xml 的数据,可能带有 cref 属性?