V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
katsusan
V2EX  ›  Go 编程语言

json 的键重复问题

  •  
  •   katsusan · 2018-11-12 16:17:59 +08:00 · 5716 次点击
    这是一个创建于 2208 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近在写个 steam 的第三方 api,其中有个接口是返回当前联赛中的比赛详细资料。url 是下面这个:

    https://api.steampowered.com/IDOTA2Match_570/GetLiveLeagueGames/v1?key=<apikey>

    但它返回的 json 里英雄描述技能升级的 key 是重复的,这个应该是不符合 json 编码标准的吧(没想到浓眉大眼的沃尔沃也干了),例如下:

    "abilities":[  // <-这样的"abilities"键总共有 5 个(代表 5 个英雄的技能升级情况)
    {
    "ability_id":5106,   //唯一技能 ID
    "ability_level":2  //技能等级
    },
    {
    "ability_id":5107,
    "ability_level":3
    }
    ]
    ,
    "abilities":[
    {
    "ability_id":5082,
    "ability_level":3
    },
    {
    "ability_id":5083,
    "ability_level":2
    }
    ]
    ,
    "abilities":[
    {
    "ability_id":5122,
    "ability_level":3
    },
    {
    "ability_id":5123,
    "ability_level":2
    },
    {
    "ability_id":5125,
    "ability_level":1
    }
    ]
    

    我尝试在结构体里用相同的 json tag 指定不同的字段但自带的 json 包好像解析不出来,求知道的大佬指点下。

    	Abilities1 []struct {
    		AbilityID    uint16 `json:"ability_id"`
    		AbilityLevel uint8  `json:"ability_level"`
    	} `json:"abilities"`
    	Abilities2 []struct {
    		AbilityID    uint16 `json:"ability_id"`
    		AbilityLevel uint8  `json:"ability_level"`
    	} `json:"abilities"`
    
    第 1 条附言  ·  2018-11-12 17:01:08 +08:00
    28 条回复    2018-11-15 10:39:37 +08:00
    TommyLemon
        1
    TommyLemon  
       2018-11-12 16:25:13 +08:00   ❤️ 1
    这都不是合法的 JSON,解析后就直接最后一个把上面的全覆盖了。
    你把这段 JSON 复制粘贴到这个网站试试,用的是 js 原生的 JSON.parse 解析
    http://apijson.org/
    zjsxwc
        2
    zjsxwc  
       2018-11-12 16:28:23 +08:00
    这个反爬虫 666 !
    zjsxwc
        3
    zjsxwc  
       2018-11-12 16:33:28 +08:00   ❤️ 1
    @zjsxwc

    如果是动态语言,简单字符串替换下把`abilities`替换为 `abilities__<index_num>`然后就可以处理了,

    golang 这种碰到就难搞了
    katsusan
        4
    katsusan  
    OP
       2018-11-12 16:37:53 +08:00
    @TommyLemon 是的,确认了的确不符合规范,但不知道 valve 这样做的用意是啥
    TommyLemon
        5
    TommyLemon  
       2018-11-12 16:39:43 +08:00
    @katsusan 估计就不是按照 application/json 格式返回的,可能是 text/plain 这种
    chengfeng1992
        6
    chengfeng1992  
       2018-11-12 16:39:45 +08:00   ❤️ 1
    dota2 ?

    这个是不是考虑自己先把 json 字符串特殊处理一下呢?

    比如,trim 后直接把 ``` "abilities": ``` 全 remove 掉,然后用 ``` [] ``` 括起来。
    katsusan
        7
    katsusan  
    OP
       2018-11-12 16:42:21 +08:00
    @zjsxwc 动态语言下的替换指的是正则匹配后替换还是,有思路的话 golang 应该实现也没问题。
    主要是询问有没有碰到过或者见过第三方 json 库支持这种情况的。
    zhujinliang
        8
    zhujinliang  
       2018-11-12 16:44:05 +08:00 via iPhone
    这种用遍历 Token 自己搞吧
    katsusan
        9
    katsusan  
    OP
       2018-11-12 16:45:01 +08:00
    @TommyLemon 是按照 json 返回的,它有个?format=xxx 参数,不指定的话就返回 json,参考网站:
    http://steamwebapi.azurewebsites.net/
    TommyLemon
        10
    TommyLemon  
       2018-11-12 16:45:52 +08:00
    看到上面的链接,随便输入 key 点了下,发现 Response Header 里:
    content-type: text/html
    TommyLemon
        11
    TommyLemon  
       2018-11-12 16:46:49 +08:00
    @katsusan 那就很奇怪了,它的库有问题啊,居然支持在同一层级有同名的 key,或者压根就没用 JSON 库解析过,直接拼出来字符串返回
    zjsxwc
        12
    zjsxwc  
       2018-11-12 16:48:19 +08:00   ❤️ 1
    @katsusan

    字符串替换都可以。

    golang 你也手动替换成合法的 json 吧,

    1. 把第一个`"abilities":[` 替换为 `"abilities":[[`

    2. 把最后一个`"ability_level":1}]` 替换为 `"ability_level":1}]]`

    3. 把剩下的 `"abilities":[` 替换为 `[`
    katsusan
        13
    katsusan  
    OP
       2018-11-12 16:50:36 +08:00
    @TommyLemon key 要去 steam 申请的,随便填应该会返回一个 forbidden 页面,所以是 html 格式,在 chrome 里可以直接看到:
    ```
    Forbidden
    Access is denied. Retrying will not help. Please verify your
    key=
    parameter.
    ```
    ihongren
        14
    ihongren  
       2018-11-12 16:50:48 +08:00
    最外层是个数组吗?
    qwertyzzz
        15
    qwertyzzz  
       2018-11-12 16:52:38 +08:00
    dota2 ! 我记得有个 opendota 和 varendata 的
    katsusan
        16
    katsusan  
    OP
       2018-11-12 17:02:35 +08:00
    @ihongren 是的,以单场比赛为单位,json 附在 append 里了
    katsusan
        17
    katsusan  
    OP
       2018-11-12 17:03:58 +08:00
    @qwertyzzz 是的,应该都是从 apisteam 里取得数据
    xqin
        18
    xqin  
       2018-11-12 17:28:20 +08:00
    都不在同一层级, 有啥问题? 哪重复了?
    一个在 "radiant" 节点下, 一个在 "dire" 节点下.

    类似于下面这样, 你能说下面的 `hello` 是重复键?
    ```
    {
    "a": {
    "hello": 123
    },

    "b": {
    "hello": 456
    }
    }
    ```


    https://imgur.com/a/sLZdgW1
    TommyLemon
        19
    TommyLemon  
       2018-11-12 17:32:56 +08:00
    @katsusan 这个我知道,一般只要 Request 能到达接口,处理后业务错误也应该返回和正确结果格式一致的 Response。
    它既然都提示 key 有问题了,就说明已经经过接口处理过了。
    katsusan
        20
    katsusan  
    OP
       2018-11-12 17:44:43 +08:00
    @xqin 每个 team 下都有 5 个 abilities 键
    ccpp132
        21
    ccpp132  
       2018-11-12 18:27:24 +08:00
    看看你用的 json 库有没有 sax 的接口,这种一般可行
    xqin
        22
    xqin  
       2018-11-12 18:46:04 +08:00
    @katsusan 你在 github 上发的那个数据, 并没有你说的这种情况.
    xqin
        23
    xqin  
       2018-11-12 18:53:10 +08:00
    @katsusan 不好意思, 看错了, 是我的那个 Chrome 插件解析之后,只剩 4 个了, 查看 原始的数据的时候, 里面是有很多个.
    vishva
        24
    vishva  
       2018-11-12 19:01:13 +08:00
    拿到 json 之后先别解析,将五个"abilities"字符串依次替换成"abilities0","abilities1"...再解析
    mengzhuo
        25
    mengzhuo  
       2018-11-12 19:25:20 +08:00   ❤️ 1
    有个东西叫

    json-to-go
    https://mholt.github.io/json-to-go/

    type AutoGenerated struct {
    Abilities []struct {
    AbilityID int `json:"ability_id"`
    AbilityLevel int `json:"ability_level"`
    } `json:"abilities"`
    }
    gamexg
        26
    gamexg  
       2018-11-12 21:47:48 +08:00   ❤️ 1
    https://github.com/buger/jsonparser

    这种库可以处理这个问题。
    katsusan
        27
    katsusan  
    OP
       2018-11-12 21:53:45 +08:00
    @mengzhuo 谢谢,这个工具不错,但刚才试了这种写法还是只会解析最后一组 abilities 数据。
    reus
        28
    reus  
       2018-11-15 10:39:37 +08:00   ❤️ 1
    这个不难处理,用标准库就行,稍微加点技巧

    https://gist.github.com/reusee/2cf9de7bbf46e6049a798630002db54b

    就是用 json.Decoder.Token 方法把 "abilities" 读掉,然后用 Decode 方法解码后面的数组,直到 Token 返回 '}'
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5908 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 39ms · UTC 02:02 · PVG 10:02 · LAX 18:02 · JFK 21:02
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.