我正试图将任何JSON对象打印为简单的HTML表单输入,并将名称属性作为JSON属性的Xpath,用点表示。
到目前为止,它是有效的,但我失去了最初的跟踪
function printInp($title, $pre, $val, $rulekey = false) {
$html = "<label for='$pre'>$pre</label>";
$html .= "<input name='$pre' value='$val' type='text' />";
return $html;
}
function printForm($title, $item, $pre = '', $rulekey=false) {
if (!is_array($item)) {
echo printInp($title, $pre, $item, $rulekey);
} else {
foreach ($item as $key => $val) {
$pre = (empty($pre)) ? $key : $pre . '.' . $key;
if (is_array($val)) {
printForm($title, $val, $pre, $key);
} else {
echo printInp($title, $pre, $val, $key);
}
$pre = '';
}
}
}
$jsonStr = json_decode('[{"id":12,"name":"live music","icon":{"url":null,"thumb":{"url":null},"ios_profile":{"url":null},"medium":{"url":null}},"display_name":null,"top_level":false}]', TRUE);
echo printForm('test', $jsonStr);
打印。。。
<label for='id'>id</label><input name='id' value='12' type='text' class='' />
<label for='name'>name</label><input name='name' value='live music' type='text' class='' />
<label for='icon.url'>icon.url</label><input name='icon.url' value='' type='text' class='' />
<label for='thumb.url'>thumb.url</label><input name='thumb.url' value='' type='text' class='' />
<label for='ios_profile.url'>ios_profile.url</label><input name='ios_profile.url' value='' type='text' class='' />
<label for='medium.url'>medium.url</label><input name='medium.url' value='' type='text' class='' />
<label for='display_name'>display_name</label><input name='display_name' value='' type='text' class='' />
<label for='top_level'>top_level</label><input name='top_level' value='' type='text' class='' />
问题是:当我遍历数组时,我失去了对顶级节点名称(在本例中为"图标")的跟踪。
应该是这样的:
<label for='id'>id</label><input name='id' value='12' type='text' class='' />
<label for='name'>name</label><input name='name' value='live music' type='text' class='' />
<label for='icon.url'>icon.url</label><input name='icon.url' value='' type='text' class='' />
<label for='icon.thumb.url'>icon.thumb.url</label><input name='icon.thumb.url' value='' type='text' class='' />
<label for='icon.ios_profile.url'>icon.ios_profile.url</label><input name='icon.ios_profile.url' value='' type='text' class='' />
<label for='icon.medium.url'>icon.medium.url</label><input name='icon.medium.url' value='' type='text' class='' />
<label for='display_name'>display_name</label><input name='display_name' value='' type='text' class='' />
<label for='top_level'>top_level</label><input name='top_level' value='' type='text' class='' />
(我将接受任何语言的答案)
这个答案适用于PHP。正如@Ghost已经评论过的,你也确认了,这需要用递归来解决。然而,在您的场景中,递归可能会有些麻烦。
因此,在PHP中,您可以通过与RecursiveIteratorIterator轻松协作,将递归转换为输入元素的平面列表。
让我们从第一个简单的例子开始,这个例子仍然不完整,但展示了它是如何工作的。这个和下面的例子将使用JSON字符串,但我对变量的命名有点不同:
$jsonStr
= <<<JSON
[
{
"id" : 12,
"name": "live music",
"icon": {
"url" : null,
"thumb" : {"url": null},
"ios_profile": {"url": null},
"medium" : {"url": null}
},
"display_name": null,
"top_level" : false
}
]
JSON;
$json = json_decode($jsonStr, true);
因此,第一个示例显示了如何使用RecursiveIteratorIterator将JSON字符串中的递归树结构转换为平面列表:
$it = new RecursiveArrayIterator($json);
$rit = new RecursiveIteratorIterator($it);
foreach ($rit as $key => $value) {
printf("%s: %s'n", $key, var_export($value, 1));
}
这会一个接一个地输出该树的所有叶节点以及关键字和值:
id: 12
name: 'live music'
url: NULL
url: NULL
url: NULL
url: NULL
display_name: NULL
top_level: false
它仍然没有您所要查找的每个节点的路径。但它已经表明,这棵树已经变成了一个列表。
现在要获得每个节点的路径,RecursiveIteratorIterator允许通过其getSubIterator
方法遍历所有键,直到当前节点的迭代器。考虑到深度信息,可以很容易地创建路径:
foreach ($rit as $key => $value) {
$jsonPathSegments = [];
for ($level = 0; $level <= $rit->getDepth(); $level++) {
$jsonPathSegments[$level] = $rit->getSubIterator($level)->key();
}
$jsonPath = implode('.', $jsonPathSegments);
printf("%s: %s'n", $jsonPath, var_export($value, 1));
}
这现在产生以下输出:
0.id: 12
0.name: 'live music'
0.icon.url: NULL
0.icon.thumb.url: NULL
0.icon.ios_profile.url: NULL
0.icon.medium.url: NULL
0.display_name: NULL
0.top_level: false
现在表明获得到每个节点的路径是容易实现的。这仍然需要一些微调,因为你实际上不想要第一个(0)级别的键。这可以通过在for
循环中将$level
初始化为1
来实现:
for ($level = 1; $level <= $rit->getDepth(); $level++) {
^
更新输出:
id: 12
name: 'live music'
icon.url: NULL
icon.thumb.url: NULL
icon.ios_profile.url: NULL
...
因此,这已经解决了的根本问题,但也允许您稍后关心输出。将其封装起来并利用__toString
甚至可以轻松地创建一个HTML小部件:
/**
* Class JsonForm
*
* Exemplary HTML Widget
*/
class JsonForm extends RecursiveIteratorIterator
{
private $json;
public function __construct($json) {
if (is_string($json) || is_object($json) && method_exists($json, '__toString')) {
$json = json_decode($json);
}
parent::__construct(new RecursiveArrayIterator($json));
}
public function getJsonPath() {
$jsonPathSegments = [];
for ($level = 1; $level <= $this->getDepth(); $level++) {
$jsonPathSegments[$level] = $this->getSubIterator($level)->key();
}
$jsonPath = implode('.', $jsonPathSegments);
return $jsonPath;
}
public function __toString() {
$html = '';
foreach ($this as $value) {
$path = $this->getJsonPath();
$html .= sprintf(
'<label for="%1$s">%1$s</label><input name="%1$s" value="%2$s" type="text" />'."'n",
htmlspecialchars($path), htmlspecialchars($value)
);
}
return $html;
}
}
这将所需的处理逻辑封装在一个简单的调用中:
echo new JsonForm($jsonStr);
产生以下示例性输出:
<label for="id">id</label><input name="id" value="12" type="text" />
<label for="name">name</label><input name="name" value="live music" type="text" />
<label for="icon.url">icon.url</label><input name="icon.url" value="" type="text" />
<label for="icon.thumb.url">icon.thumb.url</label><input name="icon.thumb.url" value="" type="text" />
<label for="icon.ios_profile.url">icon.ios_profile.url</label><input name="icon.ios_profile.url" value="" type="text" />
<label for="icon.medium.url">icon.medium.url</label><input name="icon.medium.url" value="" type="text" />
<label for="display_name">display_name</label><input name="display_name" value="" type="text" />
<label for="top_level">top_level</label><input name="top_level" value="" type="text" />
我希望这能给我们一些启示。它展示了如何通过在PHP中操作RecursiveIteratorIterator将树结构视为平面列表(参考:RecursiveITeratorItator如何在PHP中工作?)。
基于子密钥的递归结构中的路径构建已在中进行了概述
- PHP-从面包屑列表的多维关联数组返回父数组(2012年12月)
- 按值获取所有数组键(2013年2月)或
- 递归获取数组的键并创建下划线分隔的字符串(2010年5月)
很可能还有更多这样的例子。