再争论一个 json 转义的问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
lagoon
V2EX    程序员

再争论一个 json 转义的问题

  •  
  •   lagoon 2020-06-13 19:41:09 +08:00 6411 次点击
    这是一个创建于 1945 天前的主题,其中的信息可能已经有所发展或是发生改变。
    {\"身份\":\"学生\",\"姓名\":\"张三\"}

    这就是后台给的 json 原文,换种说法,也就是接口拿到的实际数据。嗯...甚至还有带\和不带的混合双打。

    众所周知这是转义,但我目前所知,dart,android 用的 gson,ios 都是无法直接将它转 json 对象的。需要先经过处理,把\去掉。
    但后台,一般觉得这是规范的 json 。


    每次遇到这个问题,都吵的不可开交。

    请大家评理。


    真的心累,不知道实际该怎么看待。因为很常见,我转不了,但后台又非常生气,觉得肯定是没问题。
    第 1 条附言    2020-06-13 21:31:52 +08:00
    因为这个,还和小群里的好友吵的面红耳赤。

    其实我知道出现这种情况,是有一些特殊原因的,还真不能怪后端,前端也不是不能处理。
    只是每次遇到,对方都一副咬定这格式没问题,非常理直气壮。导致我都怀疑是不是我的工具有问题。
    57 条回复    2020-06-17 17:53:43 +08:00
    lagoon
        1
    lagoon  
    OP
       2020-06-13 19:41:53 +08:00
    顺便一提,js 是可以的。
    msg7086
        2
    msg7086  
       2020-06-13 19:47:22 +08:00
    这不是 JSON 原文,这是把 JSON 又做了一边转义的结果。

    比如
    var a = "{\"身份\":\"学生\",\"姓名\":\"张三\"}"

    a 的内容是什么?
    是 {"身份":"学生","姓名":"张三"}

    上面 var a 后面的是为了把 JSON 表示成字符串而做的一层额外转义。变量内部是没有斜线的。

    所以后台要么给
    "{\"身份\":\"学生\",\"姓名\":\"张三\"}"
    也就是两次序列化,

    要么给
    {"身份":"学生","姓名":"张三"}
    也就是一次序列化。
    also24
        3
    also24  
       2020-06-13 19:48:13 +08:00
    要不要试试先把它当 JSON String 而不是 JSON Object

    https://i.loli.net/2020/06/13/WPlr4MVNJE7XxmZ.png
    lagoon
        4
    lagoon  
    OP
       2020-06-13 19:49:46 +08:00
    @msg7086 但我遇到过的后台哥,全部非常肯定,这就是 json,肯定能转。
    also24
        5
    also24  
       2020-06-13 19:50:36 +08:00
    我的描述好像容易引起歧义,我重洗说一下
    lagoon
        6
    lagoon  
    OP
       2020-06-13 19:54:48 +08:00
    @also24 所以我没敢回复,有点看不懂
    EminemW
        7
    EminemW  
       2020-06-13 19:56:12 +08:00
    你这个是字符串吧。。你把字符串转成 JSON 对象不就好了
    nc4697
        8
    nc4697  
       2020-06-13 19:56:54 +08:00
    这不叫 JSON 。这是 JSON 格式的字符串
    also24
        9
    also24  
       2020-06-13 19:57:58 +08:00
    @lagoon #6
    我的锅,这个专业撞上了 Python 的转义,没能表达出我的意思

    我还是直接写文本吧。

    按道理来说,你可以在这个字符串外面再套一层就好理解了

    {
    "str": "{\"身份\":\"学生\",\"姓名\":\"张三\"}"
    }

    大意如上,服务器返回的内容被当作嵌套了一层 JSON String,这样再拿 str 字段出来的时候,就会处理掉转义了。

    实际上大意和二楼是一致的,只是我不小心举了个错误的例子。
    lagoon
        10
    lagoon  
    OP
       2020-06-13 20:00:52 +08:00
    @also24 我在 dart 和 java 上的体验,拿出来,就是{\"身份\":\"学生\",\"姓名\":\"张三\"},除非自己手动转义。所以尴尬
    kojirou
        11
    kojirou  
       2020-06-13 20:00:58 +08:00
    ?
    let str = """
    {\"身份\":\"学生\",\"姓名\":\"张三\"}
    """
    dump(try JSONSerialization.jsonObject(with: Data(str.utf8), options: []))
    kojirou
        12
    kojirou  
       2020-06-13 20:02:55 +08:00
    不好意思看错了,无视我
    FutherAll
        13
    FutherAll  
       2020-06-13 20:03:35 +08:00 via iPhone
    就是个 JSON string,iOS 可以转
    jorneyr
        14
    jorneyr  
       2020-06-13 20:04:27 +08:00
    把后台给的 JSON 字符串放到 http://json.cn 里试试,如果报错说明不是 JSON 字符串,如果不报错说明正确的 JSON 字符串,可以多试几个这样的工具,用事实解决争论。
    lagoon
        15
    lagoon  
    OP
       2020-06-13 20:07:23 +08:00
    @jorneyr 没用。这招早用过了。
    他们一口咬定可以。
    jorneyr
        16
    jorneyr  
       2020-06-13 20:11:10 +08:00
    @lagoon 那这一家不是正确与否的问题了,而是人的问题
    lagoon
        17
    lagoon  
    OP
       2020-06-13 20:14:30 +08:00
    @jorneyr 但不是一个人,是我遇到的 n 位后台....
    lagoon
        18
    lagoon  
    OP
       2020-06-13 20:15:56 +08:00
    @FutherAll
    我估计你是类似
    var str = "{\"身份\":\"学生\",\"姓名\":\"张三\"}"
    这样去验证的对吧?

    不是的,这样你断点看看 str,\已经被转义了。json 处理时没有\了
    also24
        19
    also24  
       2020-06-13 20:28:09 +08:00
    重新敲了个例子,不知道这样是否好理解点

    https://i.loli.net/2020/06/13/eE1zGyB2M9lUNoX.png
    zhuangzhuang1988
        20
    zhuangzhuang1988  
       2020-06-13 20:30:51 +08:00
    直接 base64 把.
    dasauto
        21
    dasauto  
       2020-06-13 20:36:09 +08:00 via iPhone
    我之前在项目上也碰到过,手动狗头
    lagoon
        22
    lagoon  
    OP
       2020-06-13 20:37:21 +08:00
    @dasauto 真的是很常见。我已经遇到不止一次了。
    crella
        23
    crella  
       2020-06-13 20:38:53 +08:00 via Android
    代码

    # -*- encoding : utf-8 -*-
    require 'json'
    str = '{\"身份\":\"学生\",\"姓名\":\"张三\"}'
    a = JSON.load(str)
    puts a.inspect

    结果报错。
    /usr/local/ruby-2.7.0/lib/ruby/2.7.0/json/common.rb:156:in `parse': 783: unexpected token at '{\\"身份\\":\\"学生\\",\\"姓名\\":\\"张三\\"}' (JSON::ParserError)
    from /usr/local/ruby-2.7.0/lib/ruby/2.7.0/json/common.rb:156:in `parse'
    watzds
        24
    watzds  
       2020-06-13 20:39:53 +08:00 via Android
    可能是后端手写的 json,笑死人了,有些人不知道序列化
    Trim21
        25
    Trim21  
       2020-06-13 20:41:35 +08:00 via Android
    混合双打出来还能正确 decode 吗…
    also24
        26
    also24  
       2020-06-13 20:44:09 +08:00
    @crella #23
    还要处理掉 python 本身对 \ 的转义,所以应该是两个 \
    dasauto
        27
    dasauto  
       2020-06-13 20:56:25 +08:00 via iPhone
    @lagoon
    这种真的是想骂人
    nicebird
        28
    nicebird  
       2020-06-13 21:04:59 +08:00
    难道手写的 json???
    ipwx
        29
    ipwx  
       2020-06-13 21:06:19 +08:00
    这问题还用争论?把这个字符串扔进文本文件,让后端读出来自己用标准 JSON Parser 解析,看看他们行不行。这还能不闭嘴?能解析除非是调用了 eval 。
    ipwx
        30
    ipwx  
       2020-06-13 21:06:34 +08:00
    如果用了 eval 就更简单了,给他们展示一下脚本注入。
    FutherAll
        31
    FutherAll  
       2020-06-13 21:11:10 +08:00
    @lagoon 还真是,默认转义了\
    不过我还是没理解,从接口拿到的数据是啥格式的,var str = "{\"身份\":\"学生\",\"姓名\":\"张三\"}"
    这种默认转移掉不是可以解析么,是带了两个\\么
    maobukui
        32
    maobukui  
       2020-06-13 21:12:21 +08:00
    转义的目的为了防止语法上的歧义。
    没有语法上歧义的转义其实就是多此一举,增加数据交互的冗余工作。
    楼主的这个 json 格式,如果场景是 Foo = {\"身份\":\"学生\",\"姓名\":\"张三\"},这时的转义其实是多余的。
    而假设是 Foo = {"data": "{\"身份\":\"学生\",\"姓名\":\"张三\"}"},转义是需要的。
    ipwx
        33
    ipwx  
       2020-06-13 21:15:23 +08:00
    @maobukui 。。。。 你们是认真的?谁在和你们说写代码的事情?

    假设有个 JSON 存在文本文件里面,文件内容是:

    {\"身份\":\"学生\",\"姓名\":\"张三\"}

    这就根本不符合 JSON 标准啊。你读出来用啥库解析?怕不是当做 JS 执行一遍才行?
    lagoon
        34
    lagoon  
    OP
       2020-06-13 21:15:25 +08:00
    @FutherAll 其实,我估计实际是\\\,所以最终还是保留了\
    lagoon
        35
    lagoon  
    OP
       2020-06-13 21:15:50 +08:00
    @ipwx 但他们确认,这就是符合 json 规范的。。。吵翻了
    lagoon
        36
    lagoon  
    OP
       2020-06-13 21:19:07 +08:00
    @ipwx 不,他们觉得转义合情合理。
    争论,他们的办法是:
    一,直接字符串,var = {\"身份\":\"学生\",\"姓名\":\"张三\"}; 这样实际字符串就没有\了。 可以转。
    二、经过几道转换,然后把\转没了,然后再转 json 。论证没问题。
    ipwx
        37
    ipwx  
       2020-06-13 21:24:04 +08:00
    @lagoon 对啊,评论区就有这种奇葩评论。我也是醉了。
    lagoon
        38
    lagoon  
    OP
       2020-06-13 21:27:30 +08:00
    @Trim21 先把其他的转了,然后再把这个处理一下,再转。

    没办法。。。。
    ralu
        39
    ralu  
       2020-06-13 21:44:47 +08:00   1
    https://gist.github.com/Muscipular/24674f3f05b6729bb66470528387ad58
    @lagoon
    压根没毛病
    json 原文

    {
    "code": 200,
    "data": {
    "specs": "[{\"name\":\"容量\",\"values\":[\"120G\",\"260G\"]},{\"name\":\"网络型号\",\"values\":[\"移动\",\"联通\",\"全网通\"]}]"
    },
    "message": "请求成功"
    }
    ETiV
        40
    ETiV  
       2020-06-13 21:49:12 +08:00
    亲,这边没什么好争议的:是你的小伙伴他们错了。
    watzds
        41
    watzds  
       2020-06-13 22:10:20 +08:00 via Android
    @ipwx 那种 json 里套字符串 json 的,要 parse 两次
    lagoon
        42
    lagoon  
    OP
       2020-06-13 22:20:40 +08:00
    cigarzh
        43
    cigarzh  
       2020-06-13 22:24:16 +08:00
    https://www.json.org/ 在 A4 纸上打印出来然后狠狠的甩在他脸上
    fundon
        44
    fundon  
       2020-06-13 22:40:03 +08:00
    很明显后端序列化了两次后输出,不是拿一个对象(字典、Map )去序列化,而是拿字符串去序列号
    ipwx
        45
    ipwx  
       2020-06-13 22:58:14 +08:00
    @watzds 那你为啥要这么干?而且套了一个字符串,读出来的值不是带斜杠的啊。。。楼主当然指的是值就有斜杆。
    watzds
        46
    watzds  
       2020-06-13 23:24:06 +08:00 via Android
    @ipwx 我可没这么干。
    见识太少了,前后端都懂的人,对这些不会有困惑。字符串里有引号,当然会转义。

    一种情况是,后端拿到的时候就已经是 json 了,后端也不关心这个 json 的内部(可能是前端生成的配置),直接当一个对象的 string 属性,再把这个对象序列化后传给前端,没记错的话,记得奇妙清单就是这么干的
    iceheart
        47
    iceheart  
       2020-06-14 06:26:28 +08:00 via Android
    传啥都行,先写进接口文档里再搞。
    有歧义就查文档,文档没写的就补上。
    namelosw
        48
    namelosw  
       2020-06-14 09:39:31 +08:00 via iPhone
    这个是合法的 JSON.
    但是这个 JSON 的类型是字符串.
    这个字符串还是个 JSON.
    这个字符串 JSON 解析完是个对象.

    我理解这个接口是想返回这个对象,那应该返回不带转义的,不然就是序列化了两次.
    lagoon
        49
    lagoon  
    OP
       2020-06-14 10:02:37 +08:00
    @namelosw 应该是序列化了 2 次。
    如果只是一次,那么这个\就不会存在了。也不存在混合双打的情况。
    tairan2006
        50
    tairan2006  
       2020-06-14 10:15:54 +08:00
    这一般不是序列化了两次,而是把数据库里的 Json 字段当纯文本返回给客户端了。

    正确的处理方法:在返回之前反序列化一次就可以了。Java+Mybatis 用 TypeHandler 转成 JsonNode 就行,Go 自己写一个类,其他的也类似。
    lagoon
        51
    lagoon  
    OP
       2020-06-14 11:40:38 +08:00
    @tairan2006 是的,其实处理都简单。

    争论的关键点在于这个是不是符合 json 规范的。特别是混合双打的情况下。
    iEverX
        52
    iEverX  
       2020-06-14 14:09:56 +08:00 via Android
    找 json 的规范甩给他
    netnr
        53
    netnr  
       2020-06-14 15:39:26 +08:00
    在获取 JSON 对象时,一个是 字符串,一个是 object
    后端应该处理成 object,不然前端 JSON.parse 解析几次
    返回转义的 JSON 字符串是没问题,但肯定是不推荐的,你看各大服务商提供的接口,不管层级多深,肯定不会有转义字符
    Vitta
        54
    Vitta  
       2020-06-14 16:13:00 +08:00
    记得看过一个大哥发过他们的后端返回过 丨 这个字
    Hieast
        55
    Hieast  
       2020-06-14 18:00:10 +08:00
    这个返回值既不是字符串,也不是对象
    https://www.json.org/json-zh.html
    字符串应该以 `"` 开头,这个返回值不是,所以不是字符串
    对象以 `{` 开头,然后里面的 key 和 value 需要是字符串类型以 `"` 开头,但是里面的 key 和 value 都是以 `\` 开头的,所以也不是对象

    后端返回的是代码原文,得执行 eval 才能正确解析,这个风险肯定很高。这个返回值肯定不是用 json dump 的标准库处理得到的,太奇葩了
    Reficul
        56
    Reficul  
       2020-06-14 20:24:52 +08:00
    V2EX 竟然也能有这么多搞不清序列化的老哥
    rokeyzhao
        57
    rokeyzhao  
       2020-06-17 17:53:43 +08:00
    json 和 json 字符串的区别
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     6016 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 40ms UTC 02:11 PVG 10:11 LAX 19:11 JFK 22:11
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86