javascript - 将多维 JSON 打印为 html 表单输入,名称为属性的 xpath

标签 javascript php json xpath recursion

我正在尝试将任何 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) 级的 key 。这可以通过在 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 操作将树结构视为平面列表(引用:How does RecursiveIteratorIterator work in PHP?)。

之前在

中概述了基于子 key 的递归结构中路径的构建

.很可能还有更多这样的例子。

关于javascript - 将多维 JSON 打印为 html 表单输入,名称为属性的 xpath,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26456871/

相关文章:

javascript - ng-repeat 出现问题,获取 "MainCtrl is not a function, got undefined"

php - 从数组的数组中删除重复的数组

php - 如何使用 php 签署 android 应用程序?

php - 获取 "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version"

java - 如何打印 json 对象和数组值?

c - 如何使用curl发出HTTPS请求并传递json

javascript - 将绳子分成相等的两半

javascript - 获取 URL 来源和参数

javascript - 如何使用 turn.js 进行缩放

java - MrBean模块在反序列化时是否仍然自动支持动态实现简单接口(interface)?