1

My challenge: Add the ability to serialize and deserialize from/to XML to a class that only knows how to do this with JSON.

I'm new to C#, although I'm conversant with the "basic" application of Design Patterns.

A colleague suggested the following:

My recommendation is that you inject a serializer (write an IRDCSerizliazer interface that is a decorator/adapter that wraps whichever serializer it is constructed with).

They're very big on the "open/closed" principle here, which is probably why the suggestion was to use an interface and a pattern rather than adding any code to the class.

I just built a sample "Decorator" pattern in C# to make sure I understood how to do the "standard" decorator pattern and it feels way to complex for this situation... (although I'm happy to be corrected)

I think I need something that abstracts "Serializer" and allows "Serializer.serialize() and Serializer.deserialize() to be called from my worker class that currently only knows about JSON. I also think I want to be able to have either the JSON code or the XML code in "Serializer" based on how it is constructed. I.E. I'm assuming from my colleague's tip that he's built patterns like this...

I can feel this "on the tip of my tongue" as they say, but am having trouble making the leap. A "pure" Decorator pattern seems too complex. Help would be appreciated.

Please don't flame about how I should ask for more help at the office - obviously I don't want to do that or I wouldn't be asking for help here.

My worker class is below - you'll see that currently it just calls the JSON serialization and deserialization directly... and obviously that would have to change. Don't worry too much about what it does - it's tricky reflection stuff - the key thing is making it possible to serialize and deserialize with XML as well as JSON in this class using this concept of "injecting an interface".

namespace XXXXXXXXXXXX
{
    public interface IRecordSearchClientAdapter
    {
        string CallCorrectMethod(ClientServiceTypes component, string method, string request, Type[] paramTypes);
}

public class RecordSearchClientAdapter : IRecordSearchClientAdapter
{
    private IDictionary<ClientServiceTypes, object> componentMappings;

    public static IRecordSearchClientAdapter Create(IDictionary<ClientServiceTypes, object> clients)
    {
        return new RecordSearchClientAdapter(clients);
    }

    private RecordSearchClientAdapter(IDictionary<ClientServiceTypes, object> clients)
    {
        componentMappings = clients;
    }
    public string CallCorrectMethod(ClientServiceTypes component, string method, string request, Type[] paramTypes)
    {
        dynamic parsedrequest = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(request);
        //use another method to decide if it's JSON or XML, don't worry about injecting a Decorator:Interface?
        //dynamic parsedrequest = DeserializeObject(request);
        return CallCorrectMethod(component, method, parsedrequest, paramTypes);
    }

    //In this method, check for XML or JSON - then forward to the appropriate method.
    private string DeserializeObject(string request)
    {
        //stub - fixme!
        return null;
    }

    private string CallCorrectMethod(ClientServiceTypes component, string method, JObject request, Type[] paramTypes)
    {
        object client = componentMappings[component];

        MethodInfo mi = FindExactMethod(client, method, request, paramTypes);

        string response = null;

        if (paramTypes.Length == 1) // just one "request" object
        {
            var obj = DeserializeGenericObject(request, paramTypes[0]);
            var methodResponse = mi.Invoke(client, new object[] { obj });
            response = Newtonsoft.Json.JsonConvert.SerializeObject(methodResponse);
        }
        else // multiple parameters, need to grab them from the request
        {
            JEnumerable<JToken> tokens = request.Children();
            IEnumerator<JToken> tokenEnumerator = tokens.GetEnumerator();
            List<object> args = new List<object>();
            foreach (Type t in paramTypes)
            {
                if (!tokenEnumerator.MoveNext())
                {
                    throw new ArgumentException("Number of arguments in request doesn't match number of arguments in method");
                }

                args.Add(DeserializeGenericObject(tokenEnumerator.Current.First, t));
            }
            var methodResponse = mi.Invoke(client, args.ToArray());
            response = Newtonsoft.Json.JsonConvert.SerializeObject(methodResponse);
        }

        return response;
    }

    private MethodInfo FindExactMethod(object client, string method, JObject request, Type[] paramTypes)
    {
        // get the method that matches the name and type params
        MethodInfo mi = client.GetType().GetMethod(method, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance, null, paramTypes, null);
        return mi;
    }

    private object DeserializeGenericObject(JToken token, Type objectType)
    {
        Type myClassType = this.GetType();
        MethodInfo methodToCall = myClassType.GetMethod("DeserializeGenericTemplate", BindingFlags.IgnoreCase | BindingFlags.NonPublic | BindingFlags.Instance);
        MethodInfo genericMethodToCall = methodToCall.MakeGenericMethod(new Type[] { objectType });
        object newObject = genericMethodToCall.Invoke(this, new[] { token });

        return newObject;
    }

    private T DeserializeGenericTemplate<T>(JToken token)
    {
        T obj = token.ToObject<T>();
        return obj;
    }
}

}

jb62
  • 2,224
  • 2
  • 14
  • 23

2 Answers2

4

Not sure why you need this in ONE class. Your design seems like an overkill. I would have an interface to define common behavior (serialize and deserialize) and two implementation classes, one for JSON and another for XML, something like this

public interface ISerialize<T>
{
    T Deserialize<T>(string input);
    string Serialize(T type)
}

public class JsonSerializer<T> : ISerialize<T>
{
    T Deserialize<T>(string input) {...}
    string Serialize(T type) {...}
}

public class XmlSerializer<T> : ISerialize<T>
{
    T Deserialize<T>(string input) {...}
    string Serialize(T type) {...}
}

And here is how I would use it

public class Foo<T>
{
    private ISerializer<T> _serialiser;

    public Foo<T>(ISerializer<T> serialiser)
    {
        _serialiser = serialiser;
    }

    void DoFoo()
    {
        string result = serialiser.Serialize(instanceOfA_1);
        var instanceOfA_2 = serialiser.Deserialize<ClassA>(result);
    }
}

If you don't know which serializer is required - use an interface, for example, by injecting it into a constructor.

oleksii
  • 35,458
  • 16
  • 93
  • 163
  • Thanks! I'm gonna stare at this for a minute. One question. I've been spoiled by Java Spring. Specifically how do I "inject it into a constructor"? – jb62 Nov 19 '16 at 00:20
  • @jb62 I've update example to show you constructor injection. Now if you want to create an instance of Foo, you will need to pass a dependency to it - either Xml or Json serializer. Note that the DoFoo method now doesn't care which serializer you pass – oleksii Nov 19 '16 at 10:59
  • Oh - is that what we mean by injection. Literally making it something that gets passed into the constructor. I was obviously making it harder than it was... Many thanks! This is just what I needed to see to grok what was going on. Thanks for taking the time! – jb62 Nov 19 '16 at 22:23
  • This is the strategy patten here is a reference https://sourcemaking.com/design_patterns/strategy – Abd Al-rhman Taher Badary Nov 20 '16 at 01:40
  • Yes - thanks. The more I stared at this problem the more it seemed appropriate to use the strategy pattern and not decorator or adapter... Of course, my colleague may have been conflating them... When I tried it myself in Java without worrying about what he said, I came up with the strategy pattern organically -- I.E. it "felt" right for the problem. – jb62 Nov 21 '16 at 17:55
1

Although i think oleksii answer is the best design for this situation, i am posting this for reference; In your question you stated that your college wanted you to use the decorator pattern, i don't think that the decorator pattern is appropriate in your case, as the new functionality you want to add -XML Parsing- is independent from the existing class, i mean that the new functionality will not use any of the existing class features, so the strategy pattern -which is used in oleksii answer- would make more sense, in fact people often get confused between the 2 patterns, here is an answer that showed when to use what in a clear way.

Community
  • 1
  • 1
  • Very nice - thanks for that. It will round out the knowledge for others who look at this too. – jb62 Nov 21 '16 at 17:56