douxing6532 2015-09-03 06:15
浏览 265
已采纳

将(多个)JSON不同对象解析为C#类。 强类型更好吗?

I have been working on a client - server project. The server side implemented on PHP. The client implemented on C#. The websocket is used for connection between them.

So, here is the problem. Client will make a request. Json is in use for sending objects and validating against the schema. The request MUST HAVE it's name and MAY contain args. Args are like associative array (key => value).

Server will give a response. Response MAY contain args, objects, array of objects. For example, client sends a request like:

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

For this, server will reply with an AuthSuccess or AuthFailed response like:

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

If response is AuthSuccess, client will send a requst of who is online. Server must send an array of users.

And so on. So the problem is, how to store those responses on a client side. I mean, the way of creating a new object for each response type is insane. They will be hundreds of request types, and each of them requires it's own response. And any changing in structure of request will be very very hard...

Need some kind of pattern or trick. I know it's kind of a noob way... But if anyone has a better idea of implementing request/response structure, please tell it.

Best regards!

  • 写回答

3条回答 默认 最新

  • douwen5741 2015-09-03 07:53
    关注

    I'd definitely go with a new class for each request type. Yes, you may need to write a lot of code but it'll be safer. The point (to me) is who will write this code?. Let's read this answer to the end (or directly jump to last suggested option).

    In these examples I'll use Dictionary<string, string> for generic objects but you may/should use a proper class (which doesn't expose dictionary), arrays, generic enumerations or whatever you'll feel comfortable with.

    1. (Almost) Strongly Typed Classes

    Each request has its own strongly typed class, for example:

    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; }
    }
    

    Note that you may switch to a full typed approach also dropping Dictionary for Args in favor of typed classes.

    Pros

    What you saw as a drawback (changes are harder) is IMO a big benefit. If you change anything server-side then your request will fail because properties won't match. No subtle bugs where fields are left uninitialized because of typos in strings.

    It's strongly typed then your C# code is easier to maintain, you have compile-time checks (both for names and types).

    Refactoring is easier because IDE can do it for you, no need to blind search and replace raw strings.

    It's easy to implement complex types, your arguments aren't limited to plain string (it may not be an issue now but you may require it later).

    Cons

    You have more code to write at very beginning (however class hierarchy will also help you to spot out dependencies and similarities).

    2. Mixed Approach

    Common parameters (name and arguments) are typed but everything else is stored in a dictionary.

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

    With a mixed approach you keep some benefits of typed classes but you don't have to define each request type.

    Pros

    It's faster to implement than a almost/full typed approach.

    You have some degree of compile-time checks.

    You can reuse all code (I'd suppose your Request class will be also reused for Response class and if you move your helper methods - such as GetInt32() - to a base class then you'll write code once).

    Cons

    It's unsafe, wrong types (for example you retrieve an integer from a string property) aren't detected until error actually occurs at run-time.

    Changes won't break compilation: if you change property name then you have to manually search each place you used that property. Automatic refactoring won't work. This may cause bugs pretty hard to detect.

    Your code will be polluted with string constants (yes, you may define const string fields) and casts.

    It's hard to use complex types for your arguments and you're limited to string values (or types that can be easily serialized/converted to a plain string).

    3. Dynamic

    Dynamic objects let you define an object and access it properties/methods as a typed class but they will be actually dynamically resolved at run-time.

    dynamic request = new ExpandoObject();
    request.Name = "AuthenticationRequest";
    request.UserName = "test";
    

    Note that you may also have this easy to use syntax:

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

    Pros

    If you add a property to your schema you don't need to update your code if you don't use it.

    It's little bit more safe than an untyped approach. For example if request is filled with:

    request.UserName = "test";
    

    If you wrongly write this:

    Console.WriteLine(request.User);
    

    You will have a run-time error and you still have some basic type checking/conversion.

    Code is little bit more readable than completely untyped approach.

    It's easy and possible to have complex types.

    Cons

    Even if code is little bit more readable than completely untyped approach you still can't use refactoring features of your IDE and you almost don't have compile-time checks.

    If you change a property name or structure in your schema and you forget to update your code (somewhere) you will have an error only at run-time when it'll happen you use it.

    4. Auto-generated Strongly Typed Classes

    Last but best...so far we did forget an important thing: JSON has schema with which it can be validatd (see json-schema.org).

    How it can be useful? Your fully typed classes can be generated from that schema, let's take a look to JSON schema to POCO. If you don't have/don't want to use a schema you still can generate classes from JSON examples: take a look to JSON C# Class Generator project.

    Just create one example (or schema) for each request/response and use a custom code generator/build task to build C# classes from that, something like this (see also MSDN about custom build tools):

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

    Pro

    All the pros of above solutions.

    Cons

    Nothing I can think about...

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

悬赏问题

  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题
  • ¥15 请完成下列相关问题!
  • ¥15 drone 推送镜像时候 purge: true 推送完毕后没有删除对应的镜像,手动拷贝到服务器执行结果正确在样才能让指令自动执行成功删除对应镜像,如何解决?
  • ¥15 求daily translation(DT)偏差订正方法的代码
  • ¥15 js调用html页面需要隐藏某个按钮