0%

修复JSON序列化后符号变化的方案

构建JSON序列化后符号变化的方案

问题描述

在进行JSON序列化时,有时候会出现符号变化的问题,例如双引号或单引号被转义或丢失的情况。情况是这样的,我在作配置生成器过程中用到了默认配置, 属性用做内容填充对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 默认配置
const configsData = {
"1_minigame": {
host: `www.a0.com`,
data: { name: `A0`, logo: `${templateName}/a0` },
html: {
htmlTest:``,
hd_index:``,
hd_details:``,
hd_category:``,
hd_play:``,
ad0_0:``,
ad0_1:``,
ad0_2:``,
ad0_3:``,
ad0_4:``,
ad1_0:``,
ad1_1:``,
ad1_2:``,
}
}
};

期间我创建了原配置对象的深层副本,输出内容后,反引号变成了双引号,

1
2
const templateConfig = JSON.parse(JSON.stringify(configsData[templateName]));
console.log(templateConfig)

于是在做填充内容时,双引号里面的内容要是包含双引号或单引号,放到编译器编译器就会报错。

解决思路:

创建递归函数formatConfigObject,将格式化嵌套对象转换为字符串表示形式.
通过 Object.keys方法遍历 keys 数组中的每个prop属性,在遍历过程中,检查prop类型。如果值的类型是对象,并且不为 null,则说明该属性的值是一个嵌套的配置对象。函数会递归调用 formatConfigObject 方法,将缩进级别加一,并将嵌套对象的字符串表示添加到 configString 中。
如果值的类型不是对象,或者为 null,则说明该属性的值是基本类型,函数会根据值的类型进行处理。如果值的类型是字符串,则在其外部添加反引号;并添加到configString里去。

最后,函数根据当前属性的索引位置判断是否为最后一个属性,如果不是,则在 configString 的末尾添加换行符 \n。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function formatConfigObject(obj, indent = 1) {
const tabs = '\t'.repeat(indent);
let configString = '';

const keys = Object.keys(obj);
const lastKeyIndex = keys.length - 1;

keys.forEach(function (prop, index) {
const value = obj[prop];
if (typeof value === 'object' && value !== null) {
configString += tabs + prop + ': {\n' + formatConfigObject(value, indent + 1) + tabs + '},';
} else {
const formattedValue = '`' + value + '`';
configString += tabs + prop + ': ' + formattedValue + ',';
}

if (index !== lastKeyIndex) {
configString += '\n';
}
});

return configString;
}

期间饶了两个弯,第一次的想法就是遍历将转换后的字符串双引号替为反引号

1
configString = configString.replace(/"/g, "`");

这样内容会被影响到。

后面换了种想法直接修改要填充的内容的反引号和双引号为单引号,这样不仅会修改到内容,而且效率低麻烦。

反解:将字符串转为Json对象

通过之前的方法将json转为string类型,成功得到了我们想要的配置,假如直接复制到配置脚本没什么问题,但是我们要写个自动推送器,又要使用的configString字符串里子属性,这时就要把他反解开。

例如我接收到的配置String类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
host: ``,
data: { name: ``, logo: `` },
html: {
htmlTest: `test`,
hd_index: ``,
hd_details: ``,
hd_category: ``,
hd_play: ``,
ad0_0: ``,
ad0_1: ``,
ad0_2: ``,
ad0_3: ``,
ad0_4: ``,
ad1_0: ``,
ad1_1: ``,
ad1_2: ``
}
}

之前我们做的操作就是把属性内容符号转为反引号方便添加配置,考虑到还原后的配置属性里也嵌套着json结构,有点复杂。说说我的思路:

1
2
3
let dataValues = getValueBy(configString)
const parsedConfig = parseConfigString(dataValues.emptyConfig);
let njsonConfig = fillValues(parsedConfig, dataValues.values)

在接收到配置的第一时间,我们可以通过getValueBy遍历反引号内容,把反引号里值抽出来存放到values数组,顺便保留去掉值后的字符串emptyConfig,然后对emptyConfig处理为json再重新赋值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function getValueBy(config) {
//把反引号里的值抽出来放到values
let values = [];
const backtickMatches = config.match(/`([^`]*)`/g);
if (backtickMatches) {
backtickMatches.forEach(match => {
const backtickContent = match.slice(1, -1).trim();
values.push(backtickContent);
});
}
//获取去掉values后的config
let emptyConfig = getEmptyKey(config)

return { values, emptyConfig };
}

function getEmptyKey(config) {
let result = '';
let doubletag = 0

for (let i = 0; i < config.length; i++) {
if (config[i] === '`') {
result += config[i]
doubletag += 1
} else if (doubletag % 2 === 0) {
result += config[i]
}

}
return result.trim();
}

当然方法有很多,你也可以在getEmptyKey遍历过程中加个反锁再把values抽出来。

在获得values和emptyConfig后,使用parseConfigString(configString)方法将emptyConfig转为json对象,好处就是转换过程中不会影响到属性值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function parseConfigString(configString) {
// 没用左括号,退出
if (!configString.includes('{')) {
return;
}
let configObj = {};

const delBigKuo = configString.slice(1, -1);
// 按逗号切割
let keyValuePairs = delBigKuo.trim().split(',');
let temp = '';
keyValuePairs.forEach(pair => {
temp += pair;
if ((temp.match(/{/g) || []).length === (temp.match(/}/g) || []).length) {
const colonIndex = temp.indexOf(':');
if (colonIndex !== -1) {
const cleanedKey = temp.slice(0, colonIndex).replace(/`/g, '').trim();
let cleanedValue = temp.slice(colonIndex + 1).trim();

if (cleanedValue.startsWith('`') && cleanedValue.endsWith('`')) {
cleanedValue = cleanedValue.slice(1, -1);
}
if (cleanedValue[0] === '{') {
cleanedValue = parseConfigString(cleanedValue);
}
configObj[cleanedKey] = cleanedValue
}
temp = '';
} else {
temp += ',';
}
});

return configObj;
}

将转换为json的parsedConfig和values传到fillValue(jsonObj, values)重新赋值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function fillValues(jsonObj, values) {
let i = 0;
for (let key in jsonObj) {
if (jsonObj.hasOwnProperty(key)) {
if (typeof jsonObj[key] === 'object') {
jsonObj[key] = fillValues(jsonObj[key], values.slice(i)); //从values的第i位开始新数组切片
i += countValues(jsonObj[key]);
} else {
jsonObj[key] = values[i];
i++;
}
}
}
return jsonObj;
}

// 计算对象中的属性数量
function countValues(obj) {
let count = 0;
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
count++;
}
}
return count;
}

在反解过程尝试了很多种解法都失败了,因为反引号里属性值内容的不确定性会有很多影响,最直接的方法就是分开处理值和键再合并,至此完毕!!

-------------本文结束感谢您的阅读-------------