JSON对象平铺和平铺后对象化的实现学习

1.什么是JSON对象平铺与对象化

常见的json结构如下:

let jdata = {
    "a": "a1",
    "b": {
        "b1": "b11"
    },
    "c": ["c1", "c2", "c3"],
    "d": [{
        "d1": "d2"
    }]
}

平铺即用单层的 (Key,Value) 结构表示当前这种递归结构,平铺后效果如下:

{
	"a":"a1",
	"b.b1":"b11",
	"c[0]":"c1",
	"c[1]":"c2",
	"c[2]":"c3",
	"d[0].d1":"d2"
}

而平铺对象化就是将原来的平铺结构还原为原始对象。

2. 平铺的实现

json对象中的v可以是一个对象、数组、基本数值类型。而数组中的v依然满足第一条定义。

以第一节的结构为例,平铺过程既是递归的解析每个v,直到v是一个基本结构(数字、字符串、布尔值)不可拆分。随后用路径作为key,最后不可分的值作为v。

根据上述思路代码实现如下:

Object.jsonFlatten = function(data) {

    let result = {};
    function deepTraverse(obj, path) {
        
        if (obj !== Object(obj)) {
            result[path] = obj;
            return;
        }

        // 数组对象,如果前缀路径保证正确,拼接上下标访问符即可
        if (Array.isArray(obj)) {
            for (let i = 0; i < obj.length; i++) {
                deepTraverse(obj[i], path + "[" + i + "]");
            }
            return;
        }

        // json对象
        for (let i in obj) {
            if (path !== "") {
                deepTraverse(obj[i], path + "." + i);
            } else {
                deepTraverse(obj[i], i);
            }
        }
    }

    if(data !== Object(data)) {
        return data;
    }

    deepTraverse(data,"");
    return result;
}

3.平铺数据对象化的实现

平铺后得到的结果是一个单层的(key,value)结构。对象化即是将key还原成
原始的对象结构。

首先分析key的组成分如下几种:

  • 嵌套对象结构:x.y.z
  • 对象加数组: x.y[0],
  • 对象加嵌套数组: x.y[0][0]

接着我们对key进行解析,拆分处我们要的元素, 对象或数组。
以x.y[0]为例:

我们需要拆分出 x、y、[0]三个元素。由于key是一个字符串,因此需要做一个简单的词法分析。通过正则表达式,解析出需要的词。

限定对象名只能是 非 . [ ]三个字符之外的符号构成,
x 可以是一个对象 .y 结构中的y是一个对象,[0]是一个数组。

因此解析分两类:

\.?([^.\[\]]+)   解析 x 、.y对象结构
\[\d+\]          解析数组

对于一串符号,我们期待每解析出一个元素,就去还原我们需要的结构。对于x.y[0]还原过程如下:

解析出x

{
	x: {

	}
}

解析出 y

{
	x: {
		y:{

		}
	}
}

解析出[0]

{
	x: {
		y:[0]
	}
}

最后通过当前指针修改数组中的值。

这时我们发现一个问题 解析出 y -> [0]的过程,按正常顺序解析出y,没法直接判断y的value是一个对象还是数组。 因此我们需要前看一个符号进行判断。 或者转换思考,我们所填的对象均是某个key的value中存放的对象的一个key。

这样转换思考后,b[0],这种形式就可以统一进行处理了。

本例中,解析出y时,我们前看一个符号 [0],发现是数组,这时y的value填充为数组,反之填充为对象。

为了统一化处理问题,我们引入一个初始对象{} 和一个默认的key “” (换成任意不冲突符号都可以)

初始:

{
	"":
}

随后党我们解析目标key(x.y[0])时, 每遇到一个符号就相当于是前看符号的作用。

遇到x


{
	"": {
	}
}

遇到y

{
	"":{
		x:{

		}
	}
}

遇到[0]

{
	"":{
		x:{
			y:[]
		}
	}
}

最后一步填充值,原来的key,拿到值,填充到 最后的数组中[]。

上述描述清楚了思路,如问期望所述,我们期望每个符号解析到了,在字符列表中继续解析下一个符号。这个能力在不同语言中实现不同。以JavaScript为例,我们会用到正则表达式的exec方法。exec方法[1]说明如下,我们写正则表达式时,需要用到全局匹配。

exec
说明
exec() 方法的功能非常强大,它是一个通用的方法,而且使用起来也比 test() 方法以及支持正则表达式的 String 对象的方法更为复杂。

如果 exec() 找到了匹配的文本,则返回一个结果数组。否则,返回 null。此数组的第 0 个元素是与正则表达式相匹配的文本,第 1 个元素是与 RegExpObject 的第 1 个子表达式相匹配的文本(如果有的话),第 2 个元素是与 RegExpObject 的第 2 个子表达式相匹配的文本(如果有的话),以此类推。除了数组元素和 length 属性之外,exec() 方法还返回两个属性。index 属性声明的是匹配文本的第一个字符的位置。input 属性则存放的是被检索的字符串 string。我们可以看得出,在调用非全局的 RegExp 对象的 exec() 方法时,返回的数组与调用方法 String.match() 返回的数组是相同的。

但是,当 RegExpObject 是一个全局正则表达式时,exec() 的行为就稍微复杂一些。它会在 RegExpObject 的 lastIndex 属性指定的字符处开始检索字符串 string。当 exec() 找到了与表达式相匹配的文本时,在匹配后,它将把 RegExpObject 的 lastIndex 属性设置为匹配文本的最后一个字符的下一个位置。这就是说,您可以通过反复调用 exec() 方法来遍历字符串中的所有匹配文本。当 exec() 再也找不到匹配的文本时,它将返回 null,并把 lastIndex 属性重置为 0。

通过上述分析javascript实现如下:

Object.unflatten = function(data) {
    "use strict";
    if (Object(data) !== data || Array.isArray(data))
        return data;
    var regex = /\.?([^.\[\]]+)|\[(\d+)\]/g,
        resultholder = {};
    for (var p in data) {
        var cur = resultholder,
            prop = "",
            m;
        while (m = regex.exec(p)) {
            cur = cur[prop] || (cur[prop] = (m[2] ? [] : {}));
            prop = m[2] || m[1];
        }
        cur[prop] = data[p];
    }
    return resultholder[""] || resultholder;
};

4. 总结

分析平铺前的结构特点和平铺后的结构特点,在还原时利用额外数据简化前看的操作,简化代码

参考

[1]exec方法说明,https://www.w3school.com.cn/jsref/jsref_exec_regexp.asp
[2]https://stackoverflow.com/questions/19098797/fastest-way-to-flatten-un-flatten-nested-json-objects

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
对象json序列是将一个对象转换为json字符串的过程,而json反序列是将json字符串转换回对象的过程。 在大多数编程语言中,有现成的库或函数可以实现对象json字符串的序列和反序列操作。以下是一些常见的示例: Python: - 序列:使用`json.dumps()`函数将对象转换为json字符串。 ```python import json data = {"name": "John", "age": 30} json_str = json.dumps(data) print(json_str) # 输出: {"name": "John", "age": 30} ``` - 反序列:使用`json.loads()`函数将json字符串转换为对象。 ```python import json json_str = '{"name": "John", "age": 30}' data = json.loads(json_str) print(data["name"]) # 输出: John print(data["age"]) # 输出: 30 ``` Java: - 序列:使用Jackson、Gson等库将对象转换为json字符串。 ```java import com.fasterxml.jackson.databind.ObjectMapper; public class Person { private String name; private int age; // getters and setters public static void main(String[] args) throws Exception { ObjectMapper objectMapper = new ObjectMapper(); Person person = new Person(); person.setName("John"); person.setAge(30); String json = objectMapper.writeValueAsString(person); System.out.println(json); // 输出: {"name":"John","age":30} } } ``` - 反序列:使用Jackson、Gson等库将json字符串转换为对象。 ```java import com.fasterxml.jackson.databind.ObjectMapper; public class Person { private String name; private int age; // getters and setters public static void main(String[] args) throws Exception { ObjectMapper objectMapper = new ObjectMapper(); String json = "{\"name\":\"John\",\"age\":30}"; Person person = objectMapper.readValue(json, Person.class); System.out.println(person.getName()); // 输出: John System.out.println(person.getAge()); // 输出: 30 } } ``` 这只是两种常见编程语言的示例,其他编程语言也有相应的库或函数可用于对象json的序列和反序列。具体使用方法请参考对应编程语言的文档或教程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值