V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
lagoon
V2EX  ›  程序员

再争论一个 json 转义的问题

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

    这就是后台给的 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
    @ralu 👍
    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   ·   实用小工具   ·   2495 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 01:17 · PVG 09:17 · LAX 17:17 · JFK 20:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.