【问题标题】:printing multi-dimensional JSON as html form inputs with name as xpath of property将多维 JSON 打印为 html 表单输入,名称为属性的 xpath
【发布时间】:2014-12-14 22:00:50
【问题描述】:

我正在尝试将任何 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='' />

(我会接受任何语言的答案)

【问题讨论】:

  • 如果您的 json 对象级别将是无限的,您可能需要递归地遍历它们,如果级别将被修复,那么只需检查当前级别是否仍然是一个数组
  • @Ghost,级别可以达到 4 级深度(但我宁愿不围绕该限制进行设计)。我目前正在递归地做。

标签: javascript php json xpath recursion


【解决方案1】:

此答案适用于 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 操作将树结构视为平面列表(参考:How does RecursiveIteratorIterator work in PHP?)。

基于子键的递归结构中路径的构建之前已在

中进行了概述

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-21
    • 2014-02-04
    • 2017-10-25
    • 2014-02-14
    • 2014-10-12
    • 2011-04-25
    • 2015-10-23
    • 1970-01-01
    相关资源
    最近更新 更多