我正在尝试将一些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并包含所有属性孩子们?
由于
答案 0 :(得分:6)
在我的回答中,我将介绍PHP,特别是 SimpleXMLElement ,它已经是您代码的一部分。
使用 SimpleXMLElement 对JSON编码XML的基本方法与您在问题中的类似。您实例化XML对象然后 json_encode 它(Demo):
$xml = new SimpleXMLElement($buffer);
echo json_encode($xml, JSON_PRETTY_PRINT);
这会产生一个输出接近但不完全像您正在寻找的输出。那么你在这里用simplexml做的是你改变json_encode
对XML对象进行编码的标准方式。
这可以通过实现 JsonSerializable 界面的 SimpleXMLElement 的新子类型来完成。这是一个具有默认方式的类,它将如何通过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 中看到的那样,它会遍历所有属性并按名称添加前缀为&#34; _
&#34;最后是&#34; __text
&#34;通过强制转换为字符串。
对孩子的处理有点复杂,因为你需要区分单个子元素及其名称或者多个同名孩子需要额外数组:
// 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转换的Stackoverflow的另一个例子可以在这里找到:XML to JSON conversion in PHP SimpleXML。
答案 1 :(得分: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://stackoverflow.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;
}
}
答案 2 :(得分:-1)
您可以使用the lxml library for python
这是一个功能强大的工具,可让您引用元素的属性。