-1

I'm using Pusher to implement real time communication.

Here is the function in which I get the data from pusher:

    private void PusherOnConnected(object sender)
    {
        Debug.Log("Connected");
        channel.Bind("my-event", (dynamic data) =>
        {
            Debug.Log("my-event received");
            Debug.Log(data.GetType().ToString());
            Debug.Log(((JObject)data).ToString()); // <-- last line logged
            // See EDIT to see what lines have been added
        });
    }

When I send an event from pusher like this:

{
  "foo": "bar"
}

I'm not able to print from Unity. Here are the logs:

my-event received
Newtonsoft.Json.Linq.JObject
{
  "event": "my-event",
  "data": "{\r\n  \"foo\": \"bar\"\r\n}",
  "channel": "my-channel"
}

I'm trying to put it in a C# object, with the JObject.ToObject<>() method, but it does not work.

  1. Because one of the key of the JSON has the name event, this name cannot be a property of a C# object
  2. I know event and channel would be a string type, but what would be the type of data ?

How would you transform this dynamic data variable to an object, knowing that is, apparently, a JObject ?

EDIT

I tried to do what @derHugo suggests, but it still does not want to print the C# property:

PusherEvent.cs

using Newtonsoft.Json;
using System;

[Serializable]
public class PusherEvent 
{
    [JsonProperty("event")]
    public string theEvent;
    public string channel;
    public Data data;
}

[Serializable]
public class Data
{
    public string foo;
}

Inside the method receiving the pusher event (I did not try 1 and 2 simultaneously):

            PusherEvent pe = ((JObject)data).ToObject<PusherEvent>();          // 1
            PusherEvent pe = JsonConvert.DeserializeObject<PusherEvent>(data); // 2
            Debug.Log(pe.channel);

Here are my logs: enter image description here As you can see, it will not log the channel property and does not throw any errors...

EDIT 2: complete code

PusherManager.cs

using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PusherClient;
using UnityEngine;

public class PusherManager : MonoBehaviour
{
    public static PusherManager instance = null;
    private Pusher pusher;
    private Channel channel;

    async Task Start()
    {
        if (instance == null)
        {
            instance = this;
        }
        else if (instance != this)
        {
            Destroy(gameObject);
        }
        DontDestroyOnLoad(gameObject);
        await InitialisePusher();
    }

    private async Task InitialisePusher()
    {

        if (pusher == null)
        {
            pusher = new Pusher("<my-secret-key>", new PusherOptions()
            {
                Cluster = "eu",
                Encrypted = true
            });

            pusher.Error += OnPusherOnError;
            pusher.ConnectionStateChanged += PusherOnConnectionStateChanged;
            pusher.Connected += PusherOnConnected;
            channel = await pusher.SubscribeAsync("my-channel");
            channel.Subscribed += OnChannelOnSubscribed;
            await pusher.ConnectAsync();
        }
    }

    private void PusherOnConnected(object sender)
    {
        Debug.Log("Connected");
        channel.Bind("my-event", (dynamic data) =>
        {
            Debug.Log("my-event received");
            Debug.Log(data.GetType().ToString());
            Debug.Log(((JObject)data).ToString());
            PusherEvent pe = ((JObject)data).ToObject<PusherEvent>();
            // PusherEvent pe = JsonConvert.DeserializeObject<PusherEvent>(((JObject)data).ToString());
            Debug.Log(pe.channel);
        });
    }

    private void PusherOnConnectionStateChanged(object sender, ConnectionState state)
    {
        Debug.Log("Connection state changed");
    }

    private void OnPusherOnError(object s, PusherException e)
    {
        Debug.Log("Errored");
    }

    private void OnChannelOnSubscribed(object s)
    {
        Debug.Log("Subscribed");
    }

    async Task OnApplicationQuit()
    {
        if (pusher != null)
        {
            await pusher.DisconnectAsync();
        }
    }
}

SOLUTION

I finally managed to do it. The problem was in fact that the root object was a JObject whereas the data property of this object was a string, ~~ and not another JObject ~~:

// PusherManager.cs
// ...
PEvent<FireworkInfo> pe = new PEvent<FireworkInfo>(pusherEvent);
Debug.Log(pe);
// ...

// PEvent.cs

using Newtonsoft.Json.Linq;
using System;

[Serializable]
public class PEvent<T> 
{
    public string @event;
    public string channel;
    public T data;


    public PEvent(dynamic pusherEvent)
    {
        this.@event = pusherEvent["event"];
        this.channel = pusherEvent["channel"];
        this.data = JObject.Parse((string)pusherEvent["data"]).ToObject<T>();
    }

    public override string ToString()
    {
        return data.ToString();
    }
}

David Alvarez
  • 1,226
  • 11
  • 23
  • Could you post your complete code please? – derHugo Jun 19 '20 at 08:33
  • Done: full code @derHugo – David Alvarez Jun 19 '20 at 08:43
  • 1
    Is it possible that this `Bind` callback is executed on a different Thread .. maybe there is ab exception but you can't see it in the console? You could try and wrap it in `try { PusherEvent pe = ((JObject)data).ToObject(); Debug.Log(pe.channel); } catch(Exception e) { Debug.LogError($"{e.GetType} - {e.Message}/n{e.stackTrace}"); }` – derHugo Jun 19 '20 at 08:48
  • @derHugo Thank you so much, it is progressing : `Newtonsoft.Json.JsonSerializationException - Error converting value "{ "foo": "bar" }" to type 'Data'. Path 'data'.` – David Alvarez Jun 19 '20 at 08:54

1 Answers1

1

For field names which equal c# keywords you can use a verbatim string (@) and name it

public string @event;

See e.g. object to deserialize has a C# keyword

Or alternatively you can also name the field what ever you want but add an according [JsonProperty] attribute to tell JSON.NET explicitly how the according field is named in JSON

[JsonProperty("event")]
public string Event;

See e.g. Deserializing JSON responses which contain attributes that conflict with keywords


The data you showed would then simply be a nested class

[Serializable]
public class Data
{
    public string foo;
}

So your class should probably look like

[Serializable]
public class Response
{
    public string @event;
    // or
    //[JsonProperty("event)]
    //public string Event;

    public Data data;

    public string channel;
}

If you in fact need it to be dynamic since receiving different data structures from Pusher then you should checkout Deserialize JSON into C# dynamic object? in particular e.g. this answer

dynamic stuff = JObject.Parse("{ 'Name': 'Jon Smith', 'Address': { 'City': 'New York', 'State': 'NY' }, 'Age': 42 }");

string name = stuff.Name;
string address = stuff.Address.City;

For this you still need to know the names of the fields.

Or see this answer you would create a Dictionary for your fields where you could first check via ContainsKey if a certain field is even present in the received data structure.

Community
  • 1
  • 1
derHugo
  • 83,094
  • 9
  • 75
  • 115
  • Thanks for your help, it is pushing me toward the good direction, but it still unfortunately does not work. I edited my question with more infos. – David Alvarez Jun 19 '20 at 07:28