【问题标题】:JSON (JObject) to C# Object: impossible to print data received by PusherJSON (JObject) 到 C# 对象:无法打印 Pusher 接收到的数据
【发布时间】:2020-06-18 23:18:50
【问题描述】:

我正在使用 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
        });
    }

当我像这样从推送器发送事件时:

{
  "foo": "bar"
}

我无法从 Unity 打印。以下是日志:

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

我正在尝试使用 JObject.ToObject&lt;&gt;() 方法将其放入 C# 对象中,但它不起作用。

  1. 由于 JSON 的键之一具有名称 event,因此该名称不能是 C# 对象的属性
  2. 我知道eventchannelstring 类型,但data 的类型是什么?

如果知道显然是JObject,您将如何将此dynamic data 变量转换为对象?

编辑

我尝试按照@derHugo 的建议进行操作,但它仍然不想打印 C# 属性:

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;
}

在接收推送事件的方法内部(我没有同时尝试1和2):

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

这是我的日志: 如您所见,它不会记录通道属性,也不会抛出任何错误...

编辑 2:完整代码

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();
        }
    }
}

解决方案

我终于成功了。问题实际上是根对象是JObject,而该对象的data 属性是string,~~ 而不是另一个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();
    }
}

【问题讨论】:

  • 你能发布你的完整代码吗?
  • 完成:完整代码@derHugo
  • 这个Bind 回调是否有可能在不同的线程上执行.. 可能有 ab 异常但您在控制台中看不到?您可以尝试将其包装在 try { PusherEvent pe = ((JObject)data).ToObject&lt;PusherEvent&gt;(); Debug.Log(pe.channel); } catch(Exception e) { Debug.LogError($"{e.GetType} - {e.Message}/n{e.stackTrace}"); }
  • @derHugo 非常感谢,正在取得进展:Newtonsoft.Json.JsonSerializationException - Error converting value "{ "foo": "bar" }" to type 'Data'. Path 'data'.

标签: c# visual-studio unity3d pusher


【解决方案1】:

对于等于 c# 关键字的字段名称,您可以使用 verbatim string (@) 并为其命名

public string @event;

参见例如object to deserialize has a C# keyword

或者,您也可以随意命名该字段,但添加一个相应的 [JsonProperty] 属性以明确告诉 JSON.NET 相应字段如何在 JSON 中命名

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

参见例如Deserializing JSON responses which contain attributes that conflict with keywords


您展示的data 将只是一个嵌套类

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

所以你的班级应该看起来像

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

    public Data data;

    public string channel;
}

如果您实际上需要它是dynamic,因为从Pusher 接收不同的数据结构,那么您应该特别检查Deserialize JSON into C# dynamic object?,例如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;

为此,您仍然需要知道字段的名称。

或者查看this answer,您将为您的字段创建一个Dictionary,您可以首先通过ContainsKey检查某个字段是否存在于接收的数据结构中。

【讨论】:

  • 感谢您的帮助,它把我推向了好的方向,但不幸的是它仍然不起作用。我用更多信息编辑了我的问题。
猜你喜欢
  • 1970-01-01
  • 2021-06-05
  • 1970-01-01
  • 2013-03-18
  • 2019-06-29
  • 2015-01-02
  • 1970-01-01
  • 2020-09-05
  • 1970-01-01
相关资源
最近更新 更多