Reputation: 15778
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:
j = (T)JsonUtility.FromJson<T>(response.DataAsText);
(like it already is now)j = (T)callback(response.DataAsText);
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
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 ConvertAndCallback
you 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 Attribute
s 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