Olivier Pons
Olivier Pons

Reputation: 15778

How to check for parameter type?

I've made my own Ajax "generic" (cant remember how you call this principle in C#) like this (only relevant functions):

public class Ajax<T> 
{
    public delegate void CallbackAjaxFinished(T j);
    public void Get(string Url, CallbackAjaxFinished cbAjaxFinished)
    {
        /* blablah launch the Ajax call which calls back GetFinished() below */
    }
    private void GetFinished(HTTPRequest request, HTTPResponse response)
    {
        ConvertThenCallback(response, true);
    }
    private void ConvertThenCallback(HTTPResponse response, bool isGet)
    {
        try {
            /* make conversion, T could be for example class JsonData */
            j = (T)JsonUtility.FromJson<T>(response.DataAsText);
        } catch (ArgumentException) { /* Conversion problem */
            return;
        }
    }
}

That was working well, like this:

Ajax<JsonState> a = new Ajax<JsonState>();
a.Get(
    baseURL + _urlGetState, /* get Url */
    /* callback Ajax finished: */
    (JsonState j) => { /* do stuff in the callback */ }
);

The problem is that Unity's JsonUtility doesnt handle nested arrays (...).

To make a long story short, I'd like in my "generic" stuff, to pass a custom "json decode" function callback, default=null, and in the ConvertThenCallback() function, do this:

How would you do that in C#?


Update

Here's my JsonState + dependencies declaration:

[System.Serializable]
public class JsonGameDataCell
{
    public JsonGameDataBoatCurrentPlayerShot[] c;
    public JsonGameDataBoatOpponentShot[] o;
}

[System.Serializable]
public class JsonGameData
{
    public JsonGameDataStart start;
    public JsonGameDataCell[][] board;
}

[System.Serializable]
public class JsonData
{
    public string player;
    public JsonGameData game;
}

[System.Serializable]
public class JsonState
{
    public int state;
    public int state_sub;
    public string message;
    public JsonData data;
}

And my actual problem is that Unity Json utility can't decode nested arrays, so the property JsonGameDataCell[][] board is always set to null when I call JsonUtility.FromJson(). So I have to implement my own Json decoder. I'll use SimpleJSON which works very well, but I just want to use thise decoder only in that specific case.

Upvotes: 1

Views: 93

Answers (1)

mrogal.ski
mrogal.ski

Reputation: 5920

Seems to me that you're seeking something called an interface. You can use them to mark some objects so that the application "knows" how to treat them.

You can create such interface as such :

public interface ICustomJson
{
    void FromJson(string jsonString);
}

Now in your ConvertAndCallbackyou can just check if(typeof(T).GetInterfaces().Any(i => i == typeof(ICustomJson)))

If it does you can just create an instance of that class casted to ICustomJson and call FromJson(string) method.

Remember that your object can have no default ( parameterless ) constructor so you can create an uninitialized version of that class.


example ConvertAndCallback:

T result = null;
if(typeof(T).GetInterfaces().Any(i => i == typeof(ICustomJson)))
{
    result = (T)FormatterServices.GetUninitializedObject(typeof(T));
    ((ICustomJson)result).FromJson(string);
}
else
{
    result = (T)JsonUtility.FromJson<T>(response.DataAsText);
}

Another solution would be ( again ) to use an interface but only for deserializer part. This will involve another class ( or a factory ) to produce a deserializer for the specified type.

You can create an interface called IJsonSerializer with two methods inside T Deserialize(string) and string Serialize(T):

public interface IJsonSerializer<T>
{
    T Deserialize(string jsonString);
    string Serialize(T jsonObject);
}

Now having this interface, create a class which will implement this

public class JsonStateSerializer : IJsonSerializer<JsonState>
{
    public JsonState Deserialize(string jsonString)
    {
        // put your deserialization code up in here
    }

    public string Serialize(JsonState jsonObject)
    {
        // put your serialization code up in here
    }
}

Now create the default one:

public class DefaultJsonSerializer<T> : IJsonSerializer<T>
{
    public T Deserialize(string jsonString)
    {
        return (T)JsonUtility.FromJson<T>(jsonString);
    }

    public string Serialize(T jsonObject)
    {
        return JsonUtility.ToJson(jsonObject);
    }
}

Having these two would be easier to distinguish which one to use later in your ConvertAndCallback method.

Now if you want you can create some Attributes to mark which serializer will be applied to which object or use some factory for that.

I will go with the easiest method and just modify ConvertAndCallback method so that it will have another ( optional ) parameter.

void ConvertThenCallback(HTTPResponse response, bool isGet, IJsonSerializer<T> serializer = null)
{
    try 
    {
        if(serializer == null)
        {
            serializer = new DefaultJsonSerializer<T>();
        }
        j = serializer.Deserialize(response.DataAsText);
    } 
    catch (ArgumentException) 
    {
        /* Conversion problem */
        return;
    }
}

And apply this to your Get method too ( so that the serializer would be passed from public to private methods ):

public void Get(string Url, CallbackAjaxFinished cbAjaxFinished, IJsonSerializer<T> serializer = null)
{
    // whenever you call ConvertAndCallback
    ConvertAndCallback(param1, param2, serializer); // just pass the same serializer here
}

You can now use it with any kind of serializer that implements IJsonSerializer<T> interface or with the default one if none specified.

example usage:

Ajax<JsonState> a = new Ajax<JsonState>();
a.Get(
    baseURL + _urlGetState, /* get Url */
    /* callback Ajax finished: */
    (JsonState j) => { /* do stuff in the callback */ },
    new JsonStateSerializer()
);
// or if you want to use default one
Ajax<JsonState> a = new Ajax<JsonState>();
a.Get(
    baseURL + _urlGetState, /* get Url */
    /* callback Ajax finished: */
    (JsonState j) => { /* do stuff in the callback */ }
);

I don't quite understand what you meant but will answer the part I do. Please clarify in the comment if that's what you wanted or if there's something unclear in my answer.

Upvotes: 1

Related Questions