解析(许多)JSON不同对象到c#类.强类型更好
本文关键字:强类型 更好 对象 许多 JSON 解析 | 更新日期: 2023-09-27 18:10:54
我一直在做一个客户端-服务器项目。服务器端使用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>
,但您可能/应该使用适当的类(不暴露字典),数组,泛型枚举或任何您觉得舒服的。
每个请求都有自己的强类型类,例如:
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
关键字
你可以阅读更多关于动态