解析(许多)JSON不同对象到c#类.强类型更好



我一直在做一个客户端-服务器项目。服务器端使用PHP实现。客户端是用c#实现的。websocket用于它们之间的连接。

问题就在这里。客户将提出请求。Json用于发送对象和根据模式进行验证。请求必须有它的名称,并且可以包含参数。参数类似于关联数组(key => value)。

服务器将给出响应。响应可以包含参数,对象,对象数组。例如,客户端发送如下请求:

    {
    "name": "AuthenticationRequest",
    "username": "root",
    "password": "password",
    "etc": "etc"
    }

对于这种情况,服务器将返回AuthSuccess或AuthFailed响应,如:

    {
    "name": "AuthFailed",
    "args": [
        {
        "reason": "Reason text"
        }]
    }

如果response是AuthSuccess,客户端将发送一个谁在线的请求。服务器必须发送一个用户数组。

以此类推。问题是,如何在客户端存储这些响应。我的意思是,为每种响应类型创建新对象的方式太疯狂了。它们将有数百种请求类型,每种请求类型都需要自己的响应。请求结构的任何改变都是非常非常困难的…

需要某种模式或技巧。我知道这是一种新手的方式……但是如果有人有更好的实现请求/响应结构的想法,请告诉我。

致以最亲切的问候!

我肯定会为每个请求类型使用一个新类。是的,您可能需要编写大量代码,但是更安全。关键(对我来说)是谁来写这段代码?让我们把这个答案读到最后(或者直接跳到最后一个建议选项)。

在这些例子中,我将为泛型对象使用Dictionary<string, string>,但您可能/应该使用适当的类(不暴露字典),数组,泛型枚举或任何您觉得舒服的。

<标题> 1。(几乎)强类型类

每个请求都有自己的强类型类,例如:

abstract class Request {
    protected Request(string name) {
        Name = name;
    }
    public string Name { get; private set; }
    public Dictionary<string, string> Args { get; set; }
}
sealed class AuthenticationRequest : Request
{
    public AuthenticationRequest() : base("AuthenticationRequest") {
    }
    public string UserName { get; set; }
    public string Password { get; set; }
}

注意,您可以切换到完整的类型方法,并将Dictionary替换为Args,以支持类型类。

优点

你所看到的缺点(改变更困难)在我看来是一个很大的好处。如果您在服务器端更改任何内容,那么您的请求将失败,因为属性不匹配。没有微妙的错误,字段未初始化,因为字符串中的拼写错误。

它是强类型的,这样你的c#代码更容易维护,你有编译时检查(名称和类型)。

重构更容易,因为IDE可以为您做,不需要盲目搜索和替换原始字符串。

实现复杂类型很容易,你的参数不限于普通字符串(现在可能不是问题,但以后可能需要)。

缺点

一开始你有更多的代码要写(然而类层次结构也会帮助你找出依赖和相似之处)。

<标题> 2。混合方法

常用参数(名称和参数)是键入的,但其他所有内容都存储在字典中。

sealed class Request {
    public string Name { get; set; }
    public Dictionary<string, string> Args { get; set; }
    public Dictionary<string, string> Properties { get; set; }
}

使用混合方法可以保留类型化类的一些优点,但不必定义每个请求类型。

优点

它比几乎/完全类型的方法实现得更快。

你有一定程度的编译时检查。

你可以重用所有的代码(我认为你的Request类也将被重用为Response类,如果你移动你的助手方法-如GetInt32() -一个基类,然后你会写代码一次)。

缺点

不安全,错误的类型(例如从字符串属性检索整数)直到运行时实际发生错误才会被检测到。

更改不会破坏编译:如果您更改了属性名,那么您必须手动搜索使用该属性的每个位置。自动重构不起作用。这可能会导致bug难以检测。

您的代码将被字符串常量(是的,您可以定义const string字段)和强制类型转换所污染。

很难为参数使用复杂类型,并且您仅限于字符串值(或可以轻松序列化/转换为普通字符串的类型)。

<标题> 3。动态h1> 态对象允许你定义一个对象,并以类型化类的形式访问它的属性/方法,但它们实际上会在运行时动态解析。
dynamic request = new ExpandoObject();
request.Name = "AuthenticationRequest";
request.UserName = "test";

请注意,您可能也有这种易于使用的语法:

dynamic request = new {
    Name = "AuthenticationRequest",
    UserName = "test"
};

优点

如果你给你的模式添加了一个属性,如果你不使用它,你不需要更新你的代码。

它比无类型的方法更安全。例如,如果请求填充了:

request.UserName = "test";

如果你写错了:

Console.WriteLine(request.User);

你将有一个运行时错误,你仍然有一些基本的类型检查/转换。

代码比完全无类型的方法更具可读性。

创建复杂类型是很容易的,也是可能的。

缺点

即使代码比完全无类型的方法更具可读性,你仍然不能使用IDE的重构功能,而且你几乎没有编译时检查。

如果你改变了模式中的属性名或结构,而你忘记更新你的代码(在某个地方),你只会在运行时出现错误,当你使用它时。

<标题> 4。自动生成的强类型类

最后但最好的…到目前为止,我们确实忘记了一件重要的事情:JSON有可以用来验证它的模式(见jsonschema.org)。

它如何有用?可以从该模式生成完全类型的类,让我们看一下JSON模式到POCO。如果你没有/不想使用模式,你仍然可以从JSON 示例中生成类:看看JSON c#类生成器项目。

为每个请求/响应创建一个示例(或模式),并使用自定义代码生成器/构建任务从中构建c#类,类似于以下内容(参见MSDN关于自定义构建工具):

Cvent.SchemaToPoco.Console.exe -s %(FullPath) -o .%(Filename).cs -n CsClient.Model

Pro

以上方案的优点。

缺点

为什么为每一种请求/响应创建一个类是一个问题?如果您有数百种不同类型的请求和响应,您可能希望尝试更好地对它们进行分类。

我认为在你的请求或回应中有一些共同的模式。如。FailureResponse可能总是包含一些状态信息,也可能包含UserData- object(根据用例可以是任何东西)。这也同样适用于其他类别(例如:SuccessResponse)。

dynamic 是一个新的静态类型,作用类似于占位符,用于在运行时之前未知的类型。一旦声明了 dynamic 对象,就可以在其上调用操作、获取和设置属性,甚至可以像传递任何普通类型一样传递 dynamic 实例。 dynamic 给了我们很多自缢的余地。当处理在编译时可以知道类型的对象时,应该不惜一切代价避免使用 dynamic 关键字

你可以阅读更多关于动态

最新更新