iOS系统中的默认是json文本,意味着我们期望接口返回的数据格式是JSON文本

2023-12-12
来源:网络整理

问题描述

我们知道,在微信小程序中,wx. 请求参数为json,也就是说我们期望接口返回的数据格式为JSON文本,系统会自动对返回的数据进行JSON处理。 因此,如果接口返回值为 的 JSON 文本,那么我们最终得到的 res.data 应该是一个对象。

但是,一旦接口返回的数据包含行分隔符,在iOS系统上就会出现一些问题。 下面,我们通过一个简单的例子来描述这个问题。

假设微信小程序项目中有如下代码:请求接口,并假设该接口返回的数据包含行分隔符,其代码位为U+2028。

wx.request({ url: 'http://windstone.cc/test', success(res) { let data = res.data; console.log('typeof data', typeof data); if (typeof data === 'string') { for(let i = 0; i < data.length; i++) { console.log('字符是 ', data[i], 'Unicode 是 ', data[i].codePointAt(0).toString(16)); } } } })

为了模拟接口返回包含行分隔符的场景,我们将请求传递的Map函数映射到本地JSON文件,如下所示。 这是一个值为 的 JSON 文本,x 属性的值是一个仅包含代码点 U+2028 的单行分隔符的字符串。 PS:行分隔符是一个不可见的字符。 不同的平台对这个角色的展现方式不同。 您可能会看到空字符串、乱码或换行符。

{"x":" "}

在微信开发者工具中运行上述代码,会打印出以下数据:

typeof data object

这说明接口返回的数据经过JSON.之后,回调中收到的res.data是一个对象,完全符合我们的预期。

然而,当我们在iOS手机上运行上面的代码时,会打印以下数据:

typeof res.data string 字符是 { Unicode 是 7b 字符是 " Unicode 是 22 字符是 x Unicode 是 78 字符是 " Unicode 是 22 字符是 : Unicode 是 3a 字符是 " Unicode 是 22 字符是 Unicode 是 a 字符是 " Unicode 是 22 字符是 } Unicode 是 7d

res.data 返回一个字符串! 更奇怪的是,这个字符串与接口返回的JSON文本略有不同:字符串中的行分隔符(U+2028)缺失,并且在原始行分隔符对应的位置出现了换行符。 (U+000A)! (换行符的码位转换为十六进制字符串的结果是a,行分隔符是2028)

这似乎与我们之前的开发经验有些相反。 在这个场景中,与我们之前的开发场景唯一不同的是,接口返回的数据包含行分隔符。

因此,我们将返回数据修改为 {"x":1} 以验证是否是行分隔符的问题。 结果正如我们猜测的那样。 修改后,无论是在微信开发者工具上还是iOS手机上,都打印了所有数据。

进一步探索

为了进一步探究出现这种情况的原因,我们对wx. 代码,将返回的数据格式由默认的json改为text,这样返回的数据就不会自动为json了。 另外,我们再添加一些代码来尝试手动 JSON 并打印一些调试信息。

wx.request({ url: 'http://windstone.cc/test', dataType: 'text', // 设置返回的数据格式为 text success(res) { let data = res.data; console.log('typeof data', typeof data); if (typeof data === 'string') { for(let i = 0; i < data.length; i++) { console.log('字符是 ', data[i], 'Unicode 是 ', data[i].codePointAt(0).toString(16)); } // 尝试进行 JSON.parse try { data = JSON.parse(data); console.log('JSON.parse 解析成功', data); console.log('x 值的 Unicode 是 ', data.x.codePointAt(0).toString(16)); } catch(err) { console.log('JSON.parse 解析失败', err); } } } })

运行上述代码,微信开发者工具中会打印出以下数据:

typeof res.data string 字符是 { Unicode 是 7b 字符是 " Unicode 是 22 字符是 x Unicode 是 78 字符是 " Unicode 是 22 字符是 : Unicode 是 3a 字符是 " Unicode 是 22 字符是 Unicode 是 2028 字符是 " Unicode 是 22 字符是 } Unicode 是 7d JSON.parse 解析成功 {x: " "} x 值的 Unicode 是 2028

通过打印结果可以发现,在微信开发者工具中,返回的文本格式数据中的行分隔符并没有被替换,可以正常处理JSON。 在解析的对象中,x 的值仍然包含单行分隔符。 (U+2028) 字符串。

但在iOS手机上,会打印出以下数据:

typeof res.data string 字符是 { Unicode 是 7b 字符是 " Unicode 是 22 字符是 x Unicode 是 78 字符是 " Unicode 是 22 字符是 : Unicode 是 3a 字符是 " Unicode 是 22 字符是 Unicode 是 a 字符是 " Unicode 是 22 字符是 } Unicode 是 7d JSON.parse 解析失败 <SyntaxError: JSON Parse error: Unterminated string>

打印结果显示,在iOS手机上,返回的文本格式数据res.data中的行分隔符(U+2028)被替换为换行符(U+000A),以及JSON。 也会失败。

这个实验表明,当我们获取res.data时,返回的数据中的行分隔符已经被替换为换行符,以及JSON的原因。 解析失败是因为要解析的字符串包含换行符。

鉴于此,我们需要继续澄清两个问题:

为什么iOS手机上接口返回的数据中行分隔符被换行符替换? 为什么包含换行符的字符串在处理JSON时会报错?

弄清楚这两个问题之后,我们还需要解决一个问题,如何对res.data进行正确的JSON处理?

原理解析行分隔符替换为换行符

针对第一个问题,第7.3章第3行描述如下:

就像,线条用于文本和

( ) 从每个 。 , , 行有

有些超过了。 在 中,行可以是任何

两个,不过都是几个他们所受的。 一条线

任何,甚至不是。 线也的

(7.8.5)。

这些是行:

代码名称

\

换行

微信小程序读取文件内容_微信小程序读取json文件_小程序读取文件

\

\

线

\

ES3 规范规定 U+2028 和 U+2029 是行终止符,不能位于任何内容中或出现在字符串中。

正如本文中提到的,任何未编码的 U+2028 和 U+2029 都被视为换行符。

目前我还没有找到iOS系统中用换行符替换U+2028和U+2029的规范文档,但是在3的第7.8.4章的底部,有这样一句话:

注意 a 中的 A,即使是由 a 组成。 去的方法

要成为 a 的一部分的行,可以使用 \n 或 \ 等。

根据这句话,用换行符替换U+2028和U+2029也是有意义的。

因此,iOS系统可能仍然按照ES3规范处理行分隔符,将其替换为换行符,而这是在我们获取res.data字符串之前。 因此,当我们获取 res.data 字符串时,字符串中没有行分隔符,只有换行符。

BTW,在最新的 ECMA-262 第 11 版规范中,为了与 JSON 一致,允许 U+2028 和 U+2029 出现在字符串中。

包含换行符的字符串 JSON。 错误

既然iOS已经用换行符替换了行分隔符,为什么它不能仍然是JSON呢?

查看JSON规范,我们可以看到JSON文本的值可以是,,,,true,,null。

经过上一步的替换后,接口返回的JSON文本变成这样:{"x":"↵"}。 (↵仅作为换行符的示意性表示,实际上换行符是一个不可见的控制字符,无法在页面上显示)

如果字符串 {"x":"↵"} 是一个可以解析的有效 JSON 文本,那么整个 JSON 文本的值就是一个包含单个换行符的 x 属性的 JSON。 但问题是,“↵”无效。

A 是带有 (U+0022) 的代码。 所有代码都可以是必须为:标记(U+0022)、(U+005C) 和U+0000 到U+001F 的代码。 是其中的两个。

\”标记 (U+0022)。

\\ (U+005C)。

\/ (U+002F)。

\b (U+0008)。

\f 换页符 (U+000C)。

\n 换行符 (U+000A)。

\r (U+000D)。

\t (U+0009)。

根据 ECMA-404 JSON Data 中的规定,有效的 JSON 要求放在两个“, 和”之间:

代码点 U+000A 的换行符 ↵ 是控制字符。 因此,“↵”不是有效的 JSON,然后 {"x":"↵"} 也不是有效的 JSON,最终导致 JSON。 失败。

话虽如此,我们还是用更简单的方式来验证一下吧。 在浏览器控制台输入以下代码查看结果:

const a = '{"x":"\u000A"}' JSON.parse(a) // Uncaught SyntaxError: Unexpected token // in JSON at position 6 // at JSON.parse () // at :2:6

由于字符串字面量需要经过一层解析,所以解析后{"x":"\"}就变成了字符串{"x":"↵"},转换成JSON就报错。 是的,提示↵是意外的。

因此,对包含换行符的字符串执行JSON时之所以会出错,是因为换行符是控制字符,不能出现在JSON文本中。 否则,该 JSON 文本不是有效的 JSON 文本,无法成功处理。 解析。

现在我们已经弄清楚了现象和原理,接下来就是如何正确JSON包含换行符的数据了。

解决方案

再次检查 JSON 规范。 它不允许两个“之间有控制字符,但是它也说转义序列\n代表换行符。这意味着转义序列\n在JSON文本中。它代表换行符,会被解析成JSON 时的换行符。

因此,在JSON.之前,我们先通过正则表达式匹配换行符,并将换行符替换为\n,然后就可以顺利进行JSON了。

wx.request({ url: 'http://windstone.cc/test', success(res) { let data = res.data; if (typeof data === 'string') { data = data.replace(/\n/g, '\\n'); // 此处将换行符替换成 \\n,即可顺利解析 try { data = JSON.parse(data); } catch(err) { console.log('JSON.parse 解析失败', err); } } } })

这里需要注意的是,\\n是一个字符串文字,会先解析得到字符串\n,然后替换换行符。 替换后的数据为{"x":"\n"}。 JSON.之后,可以得到对象{x: '↵'}。

说明参考文档

分享