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

结构不固定的 json 值要怎么反序列化成对象比较好?

  •  1
     
  •   x97bgt · 2021-09-01 14:12:17 +08:00 · 2996 次点击
    这是一个创建于 939 天前的主题,其中的信息可能已经有所发展或是发生改变。

    json 里面有互斥的字段,要怎么序列化成对象?

    比如下面的例子里,要么只有refund字段,要么只有purchase,而且两个字段里的结构是不一样的。

    {
        "id": "1234567890",
        "amount": 100,
        "refund": {
            "date": "2021-04-23T18:25:43.511Z",
            "confirmed": true,
            "refund_id": 1234456
        }
    }
    
    {
        "id": "1234567890",
        "amount": 100,
        "purchase": {
            "date": "2021-04-23T18:25:43.511Z",
            "purchase_id": 789621
        }
    }
    

    现在到的最粗暴的办法,是把每个字段都塞进对象。但这太难看了,而且容易造成混乱。

    这种结构下,要怎么设计对象的结构比较好?

    34 条回复    2021-09-02 13:15:06 +08:00
    emeab
        1
    emeab  
       2021-09-01 14:17:57 +08:00
    应该要好好想想 为什么 id 能有重复的吧.
    xiaokongwu
        2
    xiaokongwu  
       2021-09-01 14:19:27 +08:00
    @emeab 人家只是随便写个例子吧
    binux
        3
    binux  
       2021-09-01 14:20:56 +08:00 via Android
    两个类,反序列化之前判断一下是哪个。
    wccc
        4
    wccc  
       2021-09-01 14:21:49 +08:00
    加 type 结构保持一致
    xiaokongwu
        5
    xiaokongwu  
       2021-09-01 14:23:34 +08:00
    要不用 map 接吧……
    jenlors
        6
    jenlors  
       2021-09-01 14:24:10 +08:00
    结构合并在一起,某些值可为空
    micean
        7
    micean  
       2021-09-01 14:26:21 +08:00
    自己写个自定义的反序列化适配就可以了,但是还不如塞 2 个对象呢
    YJi
        8
    YJi  
       2021-09-01 14:31:38 +08:00
    要是非用一个类,感觉只能枚举所有字段了.. (蹲一个其他方案
    thtznet
        9
    thtznet  
       2021-09-01 14:35:32 +08:00
    动态
    BigDogWang
        10
    BigDogWang  
       2021-09-01 14:36:13 +08:00
    接口设计的不合理吧,难不成你要用的时候还要判断下这两个对象哪个不为 null ?
    lower
        11
    lower  
       2021-09-01 14:39:43 +08:00
    type: 区分类型
    data:存数据,直接用 Object 或者泛型
    cpstar
        12
    cpstar  
       2021-09-01 14:40:02 +08:00
    json 本来就不是固定结构的,用编程语言的话叫不是强类型
    反序列化到对象,肯定得是两个类了,一个类是 refund,一个类是 purchase,至于这两个类是否从一个 super class 中派生,那是另外一个事情了。
    然后上工厂模式,factory.unserialize(json)
    Puteulanus
        13
    Puteulanus  
       2021-09-01 14:40:10 +08:00
    早点判断早点分开比较好吧,感觉是过度抽象了,两个东西内容不一样硬要捏在一起的话,后面写会到处是 if 判断的
    sankemao
        14
    sankemao  
       2021-09-01 14:44:43 +08:00
    加一个 type 用以区分
    chendy
        15
    chendy  
       2021-09-01 14:46:27 +08:00
    既然传进来之后要做区分,为什么不直接在接口一层就做区分呢?
    不过确实存在一些一个接口怼所有类型的情况(比如一些 webhook ),我的做法是做一个包含所有可能字段的大类接参数进来(还好没有冲突)然后区分类型转换成需要的类型然后传给对应的业务
    qinxi
        16
    qinxi  
       2021-09-01 14:46:28 +08:00
    加个 type 区分一下 然后用多态 JsonSubTypes
    Kilerd
        17
    Kilerd  
       2021-09-01 15:20:48 +08:00 via iPhone
    rust 里面的 enum 可以很好的处理这种情况。其他语言就只能自己些 deserializer 去判断 type 了
    x97bgt
        18
    x97bgt  
    OP
       2021-09-01 15:25:47 +08:00
    @wccc
    @lower
    @sankemao
    @qinxi
    加一个`type`和`data`,`type`用于判断类型,`data`则是一个抽象类或 Object,使用的时候强制转换。是这样对么?
    zhady009
        19
    zhady009  
       2021-09-01 16:11:44 +08:00
    感觉类设计有点不太合理
    建议还是根据 refund 字段是否为空来分组分开来分别序列化成对应的类型
    Jooooooooo
        20
    Jooooooooo  
       2021-09-01 16:16:09 +08:00
    弄个 type 呀.
    bnm965321
        21
    bnm965321  
       2021-09-01 16:17:15 +08:00
    union type. 在 TS 里面也能很简单的处理
    x97bgt
        22
    x97bgt  
    OP
       2021-09-01 16:31:50 +08:00
    @binux 但是方法的返回值就只有一个类型啊。。
    ysc3839
        23
    ysc3839  
       2021-09-01 17:07:09 +08:00
    如果是 C++ 的话,我会选择定义两个 struct,然后用 std::variant 。
    goHomeAdmin
        24
    goHomeAdmin  
       2021-09-01 17:07:12 +08:00
    data {
    type_1: {},
    type_2: {},
    }

    protobuf 中的 one of 使用
    ysc3839
        25
    ysc3839  
       2021-09-01 17:11:22 +08:00
    Java 的话,我会选择把两个字段都写进去,允许 null 。
    codingadog
        26
    codingadog  
       2021-09-01 17:23:56 +08:00
    这不是用一个类就能解决的么
    passerbytiny
        27
    passerbytiny  
       2021-09-01 17:32:46 +08:00 via Android
    你这个不管对应到类还是对应到关系数据,都是两种数据类型,这就不要再想反序列化成一个类了。
    passerbytiny
        28
    passerbytiny  
       2021-09-01 17:36:14 +08:00 via Android
    这种结构不稳定的数据格式,是符合 JSON 规范和接口数据规范的,但不符合面向对象数据格式,建议不要做反序列化,直接当成 JSON 读取,用 jsonpath 的形式
    jiorix
        29
    jiorix  
       2021-09-01 17:57:17 +08:00
    类设计可以更抽象点,建议把差异的字段抽象为值。
    james2013
        30
    james2013  
       2021-09-01 20:14:18 +08:00
    {
    "id": "1234567890",
    "amount": 100,
    "refund": {
    "date": "2021-04-23T18:25:43.511Z",
    "confirmed": true,
    "refund_id": 1234456
    }
    "purchase": {
    "date": "2021-04-23T18:25:43.511Z",
    "purchase_id": 789621
    }
    }

    {
    "id": "1234567890",
    "amount": 100,
    "purchase": {
    "date": "2021-04-23T18:25:43.511Z",
    "purchase_id": 789621
    }
    }
    james2013
        31
    james2013  
       2021-09-01 20:15:43 +08:00
    类中放 refund 和 purchase2 个子类就可以了
    lululau
        32
    lululau  
       2021-09-01 22:39:29 +08:00
    class RootEnt {
    String id;
    int amount;
    RefundEnt refund;
    PurchaseEnt pruchase;
    }
    mingl0280
        33
    mingl0280  
       2021-09-02 04:07:46 +08:00 via Android
    class Json
    {
    string id;
    int amount;
    Purchase purchase;
    Refund refund;
    };
    rububio
        34
    rububio  
       2021-09-02 13:15:06 +08:00 via Android
    当然是用 Discriminate Union 啊。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1053 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 19:11 · PVG 03:11 · LAX 12:11 · JFK 15:11
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.