Как я могу создать что-то вроде роутинга на websocket клиенте в Unity
я раньше писал на js и всё было очень просто. У меня была функция onmessage, там я получал data, парсил ее и из нее доставал поле route. Потом это и был по сути роут. Вот так примерно.
const routes = {
'route_1': () => {},
'route_2': () => {}
}
...
ws.onmessage = (d) => {
const data = JSON.Parse(d);
if(routes[data.route]
routes[data.route](data.data)
}
И это прекрасно работало в JS. Но как быть в C#? Я могу точно так же парсить json. Но я вот это не совсем понимаю. Там же надо создать класс, который описывает приходящие данные. Вроде такого:
class Data{
public string route;
public string data;
}
ws.onmessage = (d) => {
var data = JSON.Parse<Data>();
}
Но мне же постоянно разные по структуре данные будут приходить. Для того чтоб знать какие надо сначала распарсить и посмотреть поле route. Как это обычно принято делать? Не могу найти.
Ответы (2 шт):
Если вы используете библиотеку Newtonsoft.Json, то вам подойдёт следующий класс конверта сообщения (а если вы её не используете, то пора начинать использовать):
class Message
{
public string route;
public JToken data;
}
Также можно использовать в качестве конверта JObject.
После того, как вы определитесь с обработчиком, JToken можно десериализовать в конкретный тип данных:
interface IRouteHandler
{
void Handle(JToken data);
}
abstract class RouteHandler<T> : IRouteHandler
{
protected abstract void Handle(T data);
void IRouteHandler.Handle(JToken data)
{
this.Handle(data.ToObject<T>());
}
}
// Пример конкретного обработчика
class FooHandler : RouteHandler<Foo>
{
protected override void Handle(T data)
{
// …
}
}
Если вам ближе не ООП, а делегаты - вот так можно "замкнуть" конкретный тип в делегате:
delegate void RouteHandler(JToken data);
delegate void RouteHandler<T>(T data);
static class Route
{
public RouteHandler For<T>(RouteHandler<T> handler)
=> data => handler(data.ToObject<T>());
}
// …
Dictionary<string, RouteHandler> routes = new
{
{ "foo", Route.For<Foo>(() =>
{
// …
}
)},
{ "bar", Route.For<Bar>(() =>
{
// …
}
)},
};
Наконец, если хочется чего-то совсем примитивного и без лишних сущностей, можно сами классы сообщений сделать одновременно и обработчиками:
interface IRouteHandler
{
void Execute();
}
delegate void RouteHandler(JToken data);
static class Route
{
public RouteHandler For<T>() where T : IRouteHandler
=> data => data.ToObject<T>().Execute();
}
// …
Dictionary<string, RouteHandler> routes = new
{
{ "foo", Route.For<Foo>() },
{ "bar", Route.For<Bar>() },
};
Отмечу, что более в современной System.Text.Json вместо JToken для тех же целей нужно использовать тип JsonElement, а дальнейшая десериализация вместо ToObject делается через JsonSerializer.Deserialize<T>. В остальном всё так же.
Но мне же постоянно разные по структуре данные будут приходить.
Ну и для каждого типа ты в любом пишешь код, на любом языке. Да, структуру на JS не описываешь, но обработчики приходится.
Что касается Websocket, Unity3D тут роли не играет, чисто C#.
Некоторые инструменты C# отсутствуют, вместо них в Unity3D есть JsonUtility. Не обязательно использовать class, можно struct.
[Serializable]
public struct TileLocation
{
public Vector2Int position;
public Vector2Int vector;
}
string json; // "{ "position" : { "x": 2, "y": 3 }, "vector": { "x": 1, "y": 0 } }"
TileLocation location = JsonUtility.FromJson<TileLocation>(json);
TileLocation location = new TileLocation()
{
position = new Vector2Int(2, 3),
vector = Vector2Int.right
};
string json = JsonUtility.ToJson(location);
Объект не обязательно должен полностью соответствовать json, это не приводит к ошибкам, излишние данные игнорируются, недостающим ничего не присваивается. То есть один json можно даже делить на несколько моделей данных.
Так-же есть различные решения в Unity Asset Store.