为啥以及何时需要展平 JSON 对象?

Posted

技术标签:

【中文标题】为啥以及何时需要展平 JSON 对象?【英文标题】:Why and when do we need to flatten JSON objects?为什么以及何时需要展平 JSON 对象? 【发布时间】:2014-09-10 02:04:01 【问题描述】:

我很惊讶 *** 上没有人问过这个问题。

浏览 JSON 对象文档和快速的 google 搜索并没有产生令人满意的结果。

它有什么好处?它是如何工作的?


编辑:为了清楚起见,请看一下这个展平/非展平示例。

Fastest way to flatten / un-flatten nested JSON objects

谢谢。

【问题讨论】:

你的意思是缩小?减小文件大小,节省磁盘空间或带宽(通常是带宽)。 你指的是哪个 JSON 库? 像这样:***.com/questions/19098797/… 没有必要按照链接中的描述“展平” JSON。 (事实上​​,这有点违背 JSON 的“哲学”。)有时 JSON 的构造很差,有多余的“对象”层是不必要的,但引用的示例并非如此。 (虽然我认为这里描述的“扁平化”在某些 javascript 场景中可能有用,与 Javascript API 的关系比 JSON 本身更多。) 我在您引用的问题中要求 OP 加入这个问题。也许他能启发我们。 【参考方案1】:

在很多情况下,您会获得由某个库自动构建的 JSON 文本。在整个编程语言中,有许多构建 JSON 文本的库(一个 example is here)。

每当库添加一些额外的对象或数组包装时,您可能想要摆脱它们,可能是因为您将 JSON 发送到服务器并且您的代码在那里崩溃,因为它需要原始值而不是对象(或数组) .或者,如果您的 JSON 是服务器响应,您不希望生成的 Javascript 代码在对象/数组或非对象/数组之间有所不同。在所有这些情况下,展平很有帮助,因为它可以节省您的时间。您将不得不实现较少的 if/else,并且您可以可靠地期望您的数据结构尽可能平坦。

针对上述场景改进代码的另一种方法是以最大稳健的方式编写代码,这样就永远不会因为多余的包装而崩溃。所以总是期待一些包装器并得到它的内容。那么,就不需要展平了。

你看,这取决于构建 JSON 的内容以及解析它的内容。该建筑物可能超出您的范围。

这也会导致数据模型问题。如果某些 XY 有 0 个条目,或者某些 XY 有 >0 个条目,我已经使用过需要以不同方式解析的 XML 代码。拥有一个允许包含 0 个或多个 XY 条目的包装器将使生活更轻松。这些是数据模型的决定。

在 JSON 表示我手动组合的对象结构的所有情况下,我希望它不会改变。因此,将我详细设计的东西弄平会令人不安。据我所知,标准操作不需要展平(例如JSON.stringify()json_encode() 等)

【讨论】:

【参考方案2】:

这是一个简单的场景:在 Web 应用程序中,您有一个 HTTP POST 正在更新一个复杂的关系对象。

POST
update=1
&user.id=12345
&user.email=testmail@domain.tld
&user.profile.name=Mr. Test
&user.profile.age=42
&user.profile.friend.0.email=tom@domain.tld
&user.profile.friend.1.email=sally@domain.tld
&user.profile.friend.2.email=bob@domain.tld
&user.profile.skill.0.id=100
&user.profile.skill.0.name=javascript
&user.profile.skill.1.id=200
&user.profile.skill.1.name=piano

一切都已经在一个平面结构中,那么为什么不使用简单的一对一绑定呢?如果您有一个需要强制执行的约束或安全要求列表,您可以通过直接在排序后的键列表上搜索来验证它们。

扁平结构更易于人们理解和使用,甚至与数据库反规范化有一些交叉。它还允许以可读但更冗长的方式实现特定于上下文的安全性和约束。

当完整显示用户视图时,您可能希望隐藏用户技能列表的主键 ID 的显示。

"user.profile.skill.#.id":  hidden: true, readonly: true 

但在直接查看技能时(可能以管理员身份对其进行编辑),您可能希望查看 ID。

"skill.id":  readonly: true 

如果您正在编写一个以用户为中心/自助服务类型的 CMS 应用程序,那么您将获得更多的用户参与并能够使用简单的平面模型(底层嵌套关系模型的平面抽象)做出贡献,而不是仅仅使用嵌套模型。

TLDR:平面比嵌套更容易阅读。而程序员可以处理嵌套模式、递归解析和处理;最终用户和管理员通常更喜欢抽象出来的部分。

【讨论】:

【参考方案3】:

我意识到这是一个 5 年前的问题,但我想,我会在其中添加我的想法,以防有人遇到类似的用例并发现这很有用。

您想要展平 JSON 对象的用例之一是通过正则表达式 (RegEx) 字符串插值进行动态模板绑定。那不是一口??吗?它只是转换为“模板填充字符串而不进行硬编码”

好吧,想象一个场景,你有一个像这样的电子邮件模板字符串:

Hello firstName,

It is amazing you chose to join our site. We are happy to have you on board. 
To get started, we would really love it if you can confirm your email address
by clicking on the link: confirm_url.

Welcome aboard

The Team!

给定内存中的以下 JSON 对象:


   "user" : 
               "prefix"      : "Dr.",
               "firstName"   : "Awah",
               "lastName"    : "Teh",
               "email"       : "awah@superduperubercoolsite.com",
               "address"     : 
                                  "street": "100 Main St",
                                  "city"  : "PleasantVille",
                                  "state" : "NY",
                                  "phone" : "+1-212-555-1212"
                               
            ,
   "meta" : 
               "confirm_url" : "http://superduperubercoolsite.com/confirm/ABC123"
            

像这样进行正则表达式替换似乎超级简单(假设我们的电子邮件模板字符串存储在名为 template 的变量中,而 json 对象存储在名为 templateData 的变量中:

template = template.replace(new RegExp('firstName', 'g'), templateData.user.firstName);
template = template.replace(new RegExp('confirm_url', 'g'), templateData.meta.confirm_url);

简单吧? --> 其实是的!这封电子邮件有 10 个模板化字段怎么样,或者您想将模板与代码分离,方法是将其存储在像 SendGrid 这样的单独系统中,您的营销主管可以在其中访问模板并更改复制语言,而无需不得不打电话给工程部门的人来更改代码、测试代码并重新部署到生产环境(太麻烦了)。

这正是 JSON 扁平化的关键所在!

现在有很多方法可以展平 JSON,我有 attached a link to a codepen 我写的具有展平 JSON 的逻辑(实际上,我在 flattenJSONIntoKVPflattenJSONIntoRAW 方法中演示了两种相似但不同的方法@看看! )。

也就是说,还有其他实现,值得记住的是,这篇文章的重点是讨论 为什么 JSON 扁平化可能有用,而不是 如何强>。

继续!假设您将上面的 JSON(使用我的实现键值对)展平为如下所示:

[
    "key": "user.prefix",         "value": "Dr.",
    "key": "user.firstName",      "value": "Awah",
    "key": "user.lastName",       "value": "Teh",
    "key": "user.email",          "value": "awah@superduperubercoolsite.com",
    "key": "user.address.street", "value": "100 Main St",
    "key": "user.address.city",   "value": "PleasantVille",
    "key": "user.address.state",  "value": "NY",
    "key": "user.address.phone",  "value": "+1-212-555-1212",
    "key": "meta.confirm_url",    "value": "http://superduperubercoolsite.com/confirm/ABC123",
]

现在,我的朋友,你正在用 GAS 做饭!

为什么,因为现在您可以使用来自 JSON 对象的值动态地interpolate 模板字符串,而不必过多担心 JSON 的结构(如果它由于应用程序的发展而改变,您不必也记得来这里更改此插值代码 - 您只需更新电子邮件模板本身,请注意,它位于 SendGrid [per this example]上)。

那么你说怎么做?:简单,迭代。假设从上面展平的内容存储在一个名为 flatJSON 的变量中:


///Notice how I use Javascripts native string interpolation to create my RegExp

///Also note that I am replacing the dot (.) in my flattened JSON variable names with a double underscore (__), I only do this because my intended target is SendGrid, and I don't believe it likes dots in its template placeholders.
flatJSON.forEach(kvp=>template = template.replace(new RegExp(`$kvp.key.replace(/\./g, '__'`, 'g'), kvp.value));

就是这样,一行代码可以替换可能 10 甚至数百甚至数千(好吧..也许不是数千,但你明白了)。

哦!差点忘了,我们需要更新我们的模板字符串。

注意现在,在我们的新模板化字符串中,我们可以使用某种 FQDN 样式的变量来映射回我们的原始 JSON(理想情况下,如果 SendGrid 在其模板占位符中支持点,这看起来会非常好,但唉,不能总是赢得一切!?。

Hello user__firstName,

It is amazing you chose to join our site. We are happy to have you on board. 
To get started, we would really love it if you can confirm your email address
by clicking on the link: meta__confirm_url.

Welcome aboard user__prefix user__lastName!

The Team!

瞧瞧!

就这样,我们今天在这里完成了一些好事;我们有:

    回答了扁平化 JSON 对象的原因 我们通过codepen example 深入了解了方法 我们甚至概述了一个用例,其中利用 JSON 扁平化可以帮助您编写持久的动态代码,随着您的底层对象结构的变化而发展——这并不需要您利用丑陋的大丑 eval方法(我们可以在另一篇文章中讨论大坏丑 eval)。

【讨论】:

以上是关于为啥以及何时需要展平 JSON 对象?的主要内容,如果未能解决你的问题,请参考以下文章

何时以及为啥需要supportedRuntime 元素和sku 属性?

为啥以及何时需要在 MongoDB 中重建索引?

为啥以及何时需要在 React 中绑定函数和事件处理程序?

何时以及为啥需要在 C++ 中使用 cin.ignore()?

为啥我们需要复制构造函数以及何时应该在 java 中使用复制构造函数

将带有数组的 json 结构展平为多个没有数组的平面对象