JSON 数据结构 - JSON 到对象 - 最佳实践

Posted

技术标签:

【中文标题】JSON 数据结构 - JSON 到对象 - 最佳实践【英文标题】:JSON data structure - JSON to objects - best practice 【发布时间】:2014-03-21 15:34:17 【问题描述】:

我正在努力使用 JSON、ASP.NET、typescript/javascript 和 AngularJS 进行 Web 应用程序设计。

简而言之: 我需要一个最佳实践,通过 JSON 将数据从服务器发送到客户端,使用客户端的 JSON 字符串创建对象。

我有一个具有以下结构的 WebServerAPI 项目 (ASP.NET):

控制器 数据控制器(REST API) 型号 一个 类型

模型类:

public class A 
    public property int Id get; set;
    public property string Name get; set;
    public property Type Type get; set;


public class Type 
    public property int Id get; set;
    public property string Name get; set;

每个模型类的id都属于数据库表中的一个id(主键)。

DataController 结构:

public class DataController : ApiController 
    // ...
    // GET api/a
    public IEnumerable<A> Get()
    
        // fetches all As from the database
        // the facade creates instances of A and Type as required
        // (if A has a 'Type' the Type will also fetched an set)
        return facade.GetAll<A>();
    
    // ...

DataController 的 Get() 方法返回 JSON 结果。

JSON 结果如下所示:

[
    "Id":1,
     "Type": "Id":1, "Name":"name1"
    ,
    "Id":2,
     "Type": "Id":2, "Name":"name2"
    ,
    "Id":3,
     "Type": "Id":1, "Name":"name1"
    
    "Id":4,
     "Type": "Id":2, "Name":"name2"
    ,
    "Id":5,
     "Type": "Id":2, "Name":"name2"
    ,  
]

正如您在 JSON 数据中看到的,一些 As 共享相同的类型。虽然这是有效的 JSON,但我想知道这是否是发送数据的最佳实践。

这样发送不是更好吗:

[
    "Id":1,
     "TypeId": "Id":1
    ,
    "Id":2,
     "TypeId": "Id":2
    ,
    "Id":3,
     "TypeId": "Id":1
    
    "Id":4,
     "TypeId": "Id":2
    ,
    "Id":5,
     "TypeId": "Id":2
    ,  
]

所以我们只得到类型的 ID。但是随后我们必须请求所有可用的 Types 来确定必须在相应的 As 中设置哪个 Type。哪个可能不好?我认为这可能会很慢,因为我必须发送两个查询。

第三种选择可能是在同一个 JSON 结果中发送所有可用的类型和 As。

[
    "Types": 
        [
                "Id":1, "Name":"name1",
                "Id":2, "Name":"name2",
        ]
    ,
    "As":
        [
            "Id":1,
            "TypeId": "Id":1
            ,
            "Id":2,
             "TypeId": "Id":2
            ,
            "Id":3,
             "TypeId": "Id":1
            
            "Id":4,
             "TypeId": "Id":2
            ,
            "Id":5,
             "TypeId": "Id":2
            
        ]
    
]

所以我想知道是否有最佳实践。一遍又一遍地发送相同的对象(类型)作为 A 中的嵌套对象似乎很“愚蠢”。

特别是如果我将 JSON 字符串转换为 Typescript 对象。

没有任何“存储/缓存”逻辑,我一遍又一遍地创建“相同”对象:

export class A 
    public Id: number;
    public Name: string;
    public Type: Type;

    public static fromData(data: any): A 

        var a = new A();
        a.Id = data.Id;
        a.Name = data.Name;
        a.Type = Type.fromData(data.Type);

        return a;
    


export class Type 
        public Id: number;
        public Name: string;

        public static fromData(data: any) : Type 
            var type = new Type();
            type.Id = data.Id;
            type.Name = data.Name;

            return type;
        


// AngularJS controller
export class AListCtrl 
    static $inject = ['$scope', '$http'];

    public As: A[] = [];

    constructor(private $scope, private $http) 
        $scope.AListCtrl = this;

        $http.get('http://localhost/api/a').success((data) => 
            var as: A[] = [];
            for (var i = 0; i < data.length; i++) 
                var aData = data[i];
                var a = A.fromData(aData);

                as.push(a);
            

            this.As = as;
        );
    

创建代表相同类型的不同对象似乎是错误的。因为我经常使用其他语言(C#、Java、C++)的参考资料。使用这种反序列化数据和创建对象的方式根本不允许使用引用(也许这在 web 应用程序中是错误的?)。而且我还认为,生成大量无用的对象而不是每个 Type 是浪费内存和 CPU 时间。

相当长的帖子,但我希望它能很好地解释我的问题。

总结一下: 我需要一个最佳实践,通过 JSON 将数据从服务器发送到客户端,使用客户端的 JSON 字符串创建对象。 em>

【问题讨论】:

我通常让 c# 视图模型与 javascript 视图模型完全匹配。我发现它更容易维护,导致更快的开发时间,并且在将来当我进行更改时,这没什么大不了的(对我来说,这比节省几个字节的网络流量更重要)。如果您不是在开发一个每个字节都很重要的应用程序,或者不是传递大量数据,那么您不必担心。您已经通过传递 json 字符串而不是在查看新页面时重新加载整个页面来节省大量字节。 @dhsto 不幸的是,我必须处理大量数据(大约 1kk 个对象(As))。但正如您在 typescript 代码中看到的那样,我在 C# 和 typescript 中使用相同的类结构。 【参考方案1】:

我个人认为发送的 JSON 是数据驱动的。从这个意义上说,它应该独立于客户端或您正在使用的数据库结构。

例如,如果您检索一些Foo,则 JSON 应该包含客户端 might want to use 的所有相关信息,否则您最终将重写您的数据,为每个潜在客户提供 API。我倾向于尝试从第三者的角度来看待它;

我是否需要了解有关后备数据库的信息:不需要。 我是否希望 API 轻松地为我提供大量相关信息:是的。

总结一下:

[
    "Id":1,
     "Type": "Id":1, "Name":"name1"
    ,
    "Id":2,
     "Type": "Id":2, "Name":"name2"
    ,
    "Id":3,
     "Type": "Id":1, "Name":"name1"
    
    "Id":4,
     "Type": "Id":2, "Name":"name2"
    ,
    "Id":5,
     "Type": "Id":2, "Name":"name2"
    ,  
]

在您的情况下似乎有效。

【讨论】:

我经常使用关系数据库,这种表示数据的方式不符合关系数据库的概念。所以这可能是我对这种数据表示方式不满意的原因:) @bakunin:我可以想象。这绝对是一种不同的心态。但是问自己这个问题:如果您使用其他人的 API,您真的想以关系方式查询/组合数据吗?还是您想要一个包含所有相关信息的漂亮容器?我会选择后者,当然除非数据量特别大。 @bakunin:补充:也许 Type::Id 与客户无关。例如,如果类型是只读的或不通过这种 API 调用进行编辑。如果不是,您可以省略它。 对,但另一方面,当我从数据中创建对象时,无论如何我都会映射数据。因此,我会将其映射到一个对象(使用引用),而不是 100 个具有相同内容的对象。 @bakunin:是的,一个有效的论点。但是您会为您的客户提供 javascript 来合并/组合 JSON 数据吗?好吧,我想这仍然是一个品味问题。从 API 的角度来看,最低要求是数据结构在其上下文中是清晰的。【参考方案2】:

这是一个有趣的问题,我认为没有一个适合所有可能情况的正确答案。

一般来说,我会首先选择第三个选项。它是正确的,因为它允许您不复制相同的数据(类型),它是有效的,因为所有数据都可以通过一个请求获得,这有利于减少 XHR 请求到服务器的开销。

现在,对于不同的场景,其他两个选项可能都有一些优点。例如,如果数据集很小,您可能不关心复制您的类型数据。

我会记住的一个想法是选择最适合您的前端代码的表示形式。除非这是您想与其他客户端共享的通用 API,否则我建议您让后端和前端协同工作以获得最佳速度,同时让您的代码在客户端更简单。

希望对你有帮助

【讨论】:

不错的答案 - 就像其他答案一样,如果我只将它用于内部 API,我似乎应该选择选项 3。嗯,确实是这样,所以我想我选择选项 3【参考方案3】:

API 设计的一个很好的经验法则是制作您的 json 响应以满足您的客户端需求。我相信一些 REST 纯粹主义者会不同意,但在现实世界的应用程序中,减少 XHR 请求和客户端处理比坚持教条式的资源建模方法更可取。

让您的数据库/服务器端对象模型抽象泄漏到您的客户端最终会花费您的时间和精力。如果您序列化数据以满足您的客户需求,那么即使您的后端实现发生变化,您也可以保留该接口。

【讨论】:

当我想在 typescript 中使用引用时,无论如何我都需要映射数据。因此,每个 Type 1 个对象,并且 As 引用不同的 Type 对象。但也许使用引用是错误的? 您的客户端模型不一定需要映射到您的服务器端模型。共享模型可以节省大量时间,但有时为了性能和可用性改变或扁平化模型是有意义的。 @Scoot Puleo:嗯.. 我同意这一点。我当然可以提供两种获取数据的方法。一个包含选项 1 中的所有内容,另一个用于我的 typescript/javascript 实现,它帮助我将数据映射到对象(同样,使用引用)。【参考方案4】:

我认为您需要定义对您的客户端应用程序最有意义的 JSON 表示,然后确保以该格式发送数据。我将创建一个自定义序列化程序,使用内置的JavaScriptConverter 或可能使用Json.Net 提供的序列化程序。仅仅使用内置的序列化程序似乎并不能给出理想的结果。

【讨论】:

好的,定义我自己的 json 结构对我来说也很有意义。您更喜欢哪个选项? 2个还是3个? 我会选择选项 3,因为它减少了到服务器的往返行程,并且可能(?)稍微简化您的客户端代码。

以上是关于JSON 数据结构 - JSON 到对象 - 最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

Node JS的最佳JSON或JS对象到XML转换器模块是啥[关闭]

在 MySQL 中存储相当大的 JSON 对象的最佳方法是啥

在 Rails 数据库中存储和稍后访问 JSON 对象的最佳实践

通过 Web 传输实体框架对象并通过 JSON 返回的最佳方式

JSON数据从OSS迁移到MaxCompute最佳实践

引用 JSON 树结构的分支的最佳方法是啥?