首页 > 解决方案 > JSON (JObject) 到 C# 对象:无法打印 Pusher 接收到的数据

问题描述

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

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

  1. 因为 JSON 的键之一具有名称event,所以此名称不能是 C# 对象的属性
  2. 我知道event并且channel会是一种string类型,但是 的类型是data什么?

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

编辑

我尝试按照@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();
        }
    }
}

解决方案

我终于设法做到了。问题实际上是根对象是 aJObjectdata这个对象的属性是 a 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();
    }
}

标签: c#visual-studiounity3dpusher

解决方案


对于等于 c# 关键字的字段名称,您可以使用逐字字符串 ( @)并将其命名

public string @event;

参见例如要反序列化的对象有一个 C# 关键字

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

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

参见例如反序列化包含与关键字冲突的属性的 JSON 响应


您展示的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那么您应该检查将JSON 反序列化为 C# 动态对象?特别是例如这个答案

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

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

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

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


推荐阅读