在 C#、Twitch Pubsub 中反序列化复杂的 JSON

问题描述

我从 Twitch Pubsub System 的 Channel Points Event 中得到了这个 Json 字符串。

{
"type": "reward-redeemed","data": {
  "timestamp": "2019-11-12T01:29:34.98329743Z","redemption": {
    "id": "9203c6f0-51b6-4d1d-a9ae-8eafdb0d6d47","user": {
      "id": "30515034","login": "davethecust","display_name": "davethecust"
    },"channel_id": "30515034","redeemed_at": "2019-12-11T18:52:53.128421623Z","reward": {
      "id": "6ef17bb2-e5ae-432e-8b3f-5ac4dd774668","title": "hit a gleesh walk on stream","prompt": "cleanside's finest \n","cost": 10,"is_user_input_required": true,"is_sub_only": false,"image": {
        "url_1x": "https://static-cdn.jtvnw.net/custom-reward-images/30515034/6ef17bb2-e5ae-432e-8b3f-5ac4dd774668/7bcd9ca8-da17-42c9-800a-2f08832e5d4b/custom-1.png","url_2x": "https://static-cdn.jtvnw.net/custom-reward-images/30515034/6ef17bb2-e5ae-432e-8b3f-5ac4dd774668/7bcd9ca8-da17-42c9-800a-2f08832e5d4b/custom-2.png","url_4x": "https://static-cdn.jtvnw.net/custom-reward-images/30515034/6ef17bb2-e5ae-432e-8b3f-5ac4dd774668/7bcd9ca8-da17-42c9-800a-2f08832e5d4b/custom-4.png"
      },"default_image": {
        "url_1x": "https://static-cdn.jtvnw.net/custom-reward-images/default-1.png","url_2x": "https://static-cdn.jtvnw.net/custom-reward-images/default-2.png","url_4x": "https://static-cdn.jtvnw.net/custom-reward-images/default-4.png"
      },"background_color": "#00C7AC","is_enabled": true,"is_paused": false,"is_in_stock": true,"max_per_stream": { "is_enabled": false,"max_per_stream": 0 },"should_redemptions_skip_request_queue": true
    },"user_input": "yeooo","status": "FULFILLED"
    }
  }
}

我想将此字符串转换为以下变量: 编辑:我用 Json2CSharp.com 更新了这些类

public class User
    {
        public string id { get; set; }
        public string login { get; set; }
        public string display_name { get; set; }
    }

    public class Image
    {
        public string url_1x { get; set; }
        public string url_2x { get; set; }
        public string url_4x { get; set; }
    }

    public class DefaultImage
    {
        public string url_1x { get; set; }
        public string url_2x { get; set; }
        public string url_4x { get; set; }
    }

    public class MaxPerStream
    {
        public bool is_enabled { get; set; }
        public int max_per_stream { get; set; }
    }

    public class Reward
    {
        public string id { get; set; }
        public string channel_id { get; set; }
        public string title { get; set; }
        public string prompt { get; set; }
        public int cost { get; set; }
        public bool is_user_input_required { get; set; }
        public bool is_sub_only { get; set; }
        public Image image { get; set; }
        public DefaultImage default_image { get; set; }
        public string background_color { get; set; }
        public bool is_enabled { get; set; }
        public bool is_paused { get; set; }
        public bool is_in_stock { get; set; }
        public MaxPerStream max_per_stream { get; set; }
        public bool should_redemptions_skip_request_queue { get; set; }
    }

    public class Redemption
    {
        public string id { get; set; }
        public User user { get; set; }
        public string channel_id { get; set; }
        public string redeemed_at { get; set; }
        public Reward reward { get; set; }
        public string user_input { get; set; }
        public string status { get; set; }
    }

    public class Data
    {
        public string timestamp { get; set; }
        public Redemption redemption { get; set; }
    }

    public class Root
    {
        public string type { get; set; }
        public Data data { get; set; }
    }

我尝试了不同的方法来反序列化 Json 字符串,但没有任何效果。 我的最后一次尝试如下:(已编辑)

private static void SocketMessage(object sender,MessageEventArgs e)
        {
            try
            {
                Console.WriteLine(e.Data);
                //string json = e.Data.Replace("\"{","{").Replace("\\",string.Empty);
                string json = e.Data;
                Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(json);
                Console.WriteLine(myDeserializedClass.data.redemption.reward.title);
            }
            catch(Exception ex)
            {
                Console.Write(ex);
            }
        }

.NET 框架 4.7.1

如果有人能帮助我,我将不胜感激:)

测试字符串:

{"type":"MESSAGE","data":{"topic":"channel-points-channel-v1.196174120","message":"{\"type\":\"reward-redeemed\",\"data\":{\"timestamp\":\"2021-01-04T13:36:47.746629895Z\",\"redemption\":{\"id\":\"c664b1d8-65a6-4fb9-bef0-7b90a5a3819d\",\"user\":{\"id\":\"196174120\",\"login\":\"p90ez\",\"display_name\":\"P90Ez\"},\"channel_id\":\"196174120\",\"redeemed_at\":\"2021-01-04T13:36:47.746629895Z\",\"reward\":{\"id\":\"0452f6cb-cb1c-4c8e-9978-7103d01b621a\",\"title\":\"Willkommenssound\",\"prompt\":\"Du erhälst deinen eigenen Command mit einem Sound deiner Wahl (bitte mir den Link auf Discord etc. schicken)! Für non-Subs max 15 Sekunden,für Subs bis zu 30 Sekunden.\\n(geklaut von PrideGaymer)\",\"cost\":15000,\"is_user_input_required\":false,\"is_sub_only\":false,\"image\":{\"url_1x\":\"https://static-cdn.jtvnw.net/custom-reward-images/196174120/0452f6cb-cb1c-4c8e-9978-7103d01b621a/3feba875-7151-45e8-8bf1-09d78e48baf1/custom-1.png\",\"url_2x\":\"https://static-cdn.jtvnw.net/custom-reward-images/196174120/0452f6cb-cb1c-4c8e-9978-7103d01b621a/3feba875-7151-45e8-8bf1-09d78e48baf1/custom-2.png\",\"url_4x\":\"https://static-cdn.jtvnw.net/custom-reward-images/196174120/0452f6cb-cb1c-4c8e-9978-7103d01b621a/3feba875-7151-45e8-8bf1-09d78e48baf1/custom-4.png\"},\"default_image\":{\"url_1x\":\"https://static-cdn.jtvnw.net/custom-reward-images/default-1.png\",\"url_2x\":\"https://static-cdn.jtvnw.net/custom-reward-images/default-2.png\",\"url_4x\":\"https://static-cdn.jtvnw.net/custom-reward-images/default-4.png\"},\"background_color\":\"#FF9138\",\"is_enabled\":true,\"is_paused\":false,\"is_in_stock\":true,\"max_per_stream\":{\"is_enabled\":false,\"max_per_stream\":0},\"should_redemptions_skip_request_queue\":true,\"template_id\":null,\"updated_for_indicator_at\":\"2020-02-04T23:20:28.600840418Z\",\"max_per_user_per_stream\":{\"is_enabled\":false,\"max_per_user_per_stream\":0},\"global_cooldown\":{\"is_enabled\":false,\"global_cooldown_seconds\":0},\"redemptions_redeemed_current_stream\":null,\"cooldown_expires_at\":null},\"status\":\"FULFILLED\"}}}"}}

解决方法

这个问题有很多不清楚的地方。 问题不明确,输入数据和期望没有正确解释。

后期发现问题中共享的 JSON 不是 e.Data 中的值。

无论如何,开始解决真正的问题。

e.Data 本身是一个 JSON 字符串,它有一个子属性,而该属性又是一个 JSON 字符串。

因此,需要两次 JSON 反序列化。

第一级反序列化所需的以下类。

public class MessageData
{
    public string topic { get; set; }
    public string message { get; set; }
}

public class DataRoot
{
    public string type { get; set; }
    public MessageData data { get; set; }
}

e.Data 的字符串表示 DataRoot 类的 JSON 结构。所以你需要首先反序列化 DataRoot 对象,如下所示。

var rootData = JsonConvert.DeserializeObject<DataRoot>(e.Data);

现在下一个 JSON(在问题中首先共享)在 rootData.data.message 属性中可用。要反序列化它,您需要以下类。

public class User
{
    public string id { get; set; }
    public string login { get; set; }
    public string display_name { get; set; }
}

public class Image
{
    public string url_1x { get; set; }
    public string url_2x { get; set; }
    public string url_4x { get; set; }
}

public class DefaultImage
{
    public string url_1x { get; set; }
    public string url_2x { get; set; }
    public string url_4x { get; set; }
}

public class MaxPerStream
{
    public bool is_enabled { get; set; }
    public int max_per_stream { get; set; }
}

public class Reward
{
    public string id { get; set; }
    public string channel_id { get; set; }
    public string title { get; set; }
    public string prompt { get; set; }
    public int cost { get; set; }
    public bool is_user_input_required { get; set; }
    public bool is_sub_only { get; set; }
    public Image image { get; set; }
    public DefaultImage default_image { get; set; }
    public string background_color { get; set; }
    public bool is_enabled { get; set; }
    public bool is_paused { get; set; }
    public bool is_in_stock { get; set; }
    public MaxPerStream max_per_stream { get; set; }
    public bool should_redemptions_skip_request_queue { get; set; }
}

public class Redemption
{
    public string id { get; set; }
    public User user { get; set; }
    public string channel_id { get; set; }
    public string redeemed_at { get; set; }
    public Reward reward { get; set; }
    public string user_input { get; set; }
    public string status { get; set; }
}

public class Data
{
    public string timestamp { get; set; }
    public Redemption redemption { get; set; }
}

public class Root
{
    public string type { get; set; }
    public Data data { get; set; }
}

rootData.data.message 表示上面声明的 Root 类的 JSON 结构。所以你需要反序列化它如下。

var json = rootData.data.message;
var myDeserializedClass = JsonConvert.DeserializeObject<Root>(json);

所以代码的最终版本将如下所示。

private static void SocketMessage(object sender,MessageEventArgs e)
{
    try
    {
        var rootData = JsonConvert.DeserializeObject<DataRoot>(e.Data);

        var json = rootData.data.message;

        Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(json);
        Console.WriteLine(myDeserializedClass.data.redemption.reward.title);
    }
    catch(Exception ex)
    {
        Console.Write(ex);
    }
}

希望这能帮助您解决问题。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...