2

I have a simple WCF RESTful service with only one operation which has string as a parameter void Process(string item). The item is a JSON serialized object and it could be anything.

In this particular case about 20 different classes could be sent to this service. What is the proper and handy way to deserialize those objects? How do i know know what's actually behind the JSON? I could include some Type field and do something like using Json.NET:

dynamic json = JsonConvert.DeserializeObject(input);

, examine json.Type and then deserialize input string with JsonConvert.DeserializeObject<T>(), but I am not sure that this is a good idea. Do you have any ideas?

Dayan
  • 7,634
  • 11
  • 49
  • 76
Qué Padre
  • 2,005
  • 3
  • 24
  • 39
  • 2
    possible duplicate of [Deserialize JSON into C# dynamic object?](http://stackoverflow.com/questions/3142495/deserialize-json-into-c-sharp-dynamic-object) – celerno Feb 17 '14 at 16:20
  • 1
    Are you stuck using JSON.net? Can't you use the automatic serialization that is in WCF in 4.5? – crush Feb 17 '14 at 16:21
  • 2
    You haven't made it clear why you need to accept 20 different "objects" as a parameter to a single OperationContract, but I'd guess that you are abusing the OperationContract. It would probably be best if you made multiple OperationContracts, each accepting the parameter type that you expect. – crush Feb 17 '14 at 16:23
  • @crush If you mean DataContractJsonSerializer, how can it help? – Qué Padre Feb 17 '14 at 16:23
  • @QuéPadre Well, for one, it means not polluting the intent of your OperationContract with logic that should be handled transparently. In other words, the deserialization of the incoming JSON shouldn't be a concern of your OperationContract. It doesn't directly solve your problem, but it's a step in the right direction. If you answer my second comment above, I think we could better help you. – crush Feb 17 '14 at 16:25
  • @crush it could be something else, not only web-service, for example I could be sitting on some message queue and receive json strings – Qué Padre Feb 17 '14 at 16:27
  • 1
    @celerno I don't need a dynamic object, I need a nice way to deserialize json to one of the known types. – Qué Padre Feb 17 '14 at 16:30
  • 1
    @QuéPadre It's generally not a good idea to make a single OperationContract that accepts multiple types, especially, when the type isn't shared by some base. However, if you are intent on doing it this way...you could create a Dictionary of the types, and their associated json deserializer. Then, as you mention, append the type in the incoming JSON string as a property at the root level, look up the appropriate deserializer instance from the Dictionary, and use it to produce your desired object. – crush Feb 17 '14 at 16:32
  • @crush yes, I couldn't invent something better than that, that's why I posted this question - I think this is not a good idea. Thanks, anyway! – Qué Padre Feb 17 '14 at 16:34
  • @QuéPadre In my opinion, a better way would be to create an OperationContract for each type. `ProcessType1(Type1)`, `ProcessType2(Type2)`, etc. These OperationContracts would then call your main `Process` method with the deserialized object. The technicalities just aren't clear from what you've included in your question. – crush Feb 17 '14 at 16:37
  • @crush I thought about this, but in this case I would need 20 different URI's and this helps only in case of web-service ^_^ – Qué Padre Feb 17 '14 at 16:40
  • @QuéPadre What do you mean it only helps in case of web-service? – crush Feb 17 '14 at 16:41
  • @crush sorry, as I mentioned before, for example I am listening to a queue, someone put json messages onto it. The same problem. I need to deal with them somehow. – Qué Padre Feb 17 '14 at 16:46
  • I'm thinking in an Interface for all of those classes you could receive via WFC and an implementation of MyClass.TryParse(jsonString). That would be the nicest approach, but it would require a considerable amount of coding. – celerno Feb 17 '14 at 16:46
  • @celerno for each class? ^_^ – Qué Padre Feb 17 '14 at 16:47
  • @QuéPadre When the queue sends the JSON messages to your WCF service, they should be transported as content/type application/json, no? Then, WCF would automatically deserialize them, instead of treating them like a JSON string. Are you saying that it might send an array of different objects in a single call? – crush Feb 17 '14 at 16:50
  • @crush yes, you're right. But I just meant a different case, not a web-service. An application which is listening to some queue and receives messages in json. – Qué Padre Feb 17 '14 at 16:51
  • @QuéPadre Yes, for each class. – celerno Feb 17 '14 at 16:57

1 Answers1

0

Although I would use WCF with many OperationContract methods as mentioned in comments, a dynamic way is also possible. Don't forget, many checks are omitted, and there is a lot of room to improve. Just consider it as POC work.

Suppose you have a server object to execute methods on.

public class ServerClass1
{
    public int Add(int i,int j)
    {
        return i + j; 
    }

    public string Hello()
    {
        return "HELLO";
    }
}

and you received these jsons somehow and execute dynamically

req1 : {"method":"Add","parameters":[3,5]}

var res1 = Exec(req1, new ServerClass1());

req 2: {"method":"Hello","parameters":null}

var res2 = Exec(req2, new ServerClass1());

Helper methods/classes

public class Request
{
    public string method;
    public object[] parameters;
}

public static object Exec(string json, object target)
{
    var req = JsonConvert.DeserializeObject<Request>(json);

    var method = target.GetType().GetMethod(req.method, BindingFlags.Public | BindingFlags.Instance);
    if (method == null) throw new InvalidOperationException();

    object[] parameters = new object[0];
    if (req.parameters != null)
    {
        parameters = method.GetParameters().Zip(req.parameters, (m, p) => Convert.ChangeType(p, m.ParameterType)).ToArray();
    }

    return method.Invoke(target, parameters);
}
L.B
  • 114,136
  • 19
  • 178
  • 224
  • well, it's cool, but I can not write generic method here. I need to know the exact object type and pass deserialized object to some strategy. For example, I can receive different commands "get me some beer", "start playing The Beatles" and so on... one command can carry tons of parameters in json, another could be simple as hell – Qué Padre Feb 17 '14 at 17:03
  • 1
    @QuéPadre Easy to fix. Remove `T` and make the method return an object. – L.B Feb 17 '14 at 17:16