我如何使用字典解析这个JSON?

时间:2015-02-04 18:12:43

标签: c# json json.net steam steam-web-api

我正在制作一个需要解析JSON响应的程序。响应中的大多数数据都没用,但我需要用它来做一些事情。

  1. 能够从id部分检索rgInventory的搜索,然后获取相应的classid
  2. 使用此market_name
  3. 获取对象的classid

    JSON响应(摘录,它继续使用该格式):

    {
       "success":true,
       "rgInventory":{
          "1482735510":{
             "id":"1482735510",
             "classid":"469449975",
             "instanceid":"0",
             "amount":"1",
             "pos":1
          },
          "1468698711":{
             "id":"1468698711",
             "classid":"619638799",
             "instanceid":"0",
             "amount":"1",
             "pos":2
          },
       },
       "rgCurrency":[
       ],
       "rgDescriptions":{
          "469449975_0":{
             "appid":"730",
             "classid":"469449975",
             "instanceid":"0",
             "icon_url":"fWFc82js0fmoRAP-qOIPu5THSWqfSmTELLqcUywGkijVjZYMUrsm1j-9xgEObwgfEh_nvjlWhNzZCveCDfIBj98xqodQ2CZknz5oM7bgZghmfzvDE61HY-Yy_QbpNis77893GtbmoLpffljq4tCXNLN9ZY0fSZPVCaWPZQ_5v0tshKIJK5KBqSjs2i73ejBdAx_EB8I",
             "icon_url_large":"fWFc82js0fmoRAP-qOIPu5THSWqfSmTELLqcUywGkijVjZYMUrsm1j-9xgEObwgfEh_nvjlWhNzZCveCDfIBj98xqodQ2CZknz5oM7bgZghmfzvDE61HY-Yy_QbpNis77893a9u35bwDZ13vs9PPNOQpZoodGMOBD6PVMFr4uRgxg6dZepXdpCm72SrhM2wJXBD1ujVT-Ntzxu8",
             "icon_drag_url":"",
             "name":"SG 553 | Army Sheen",
             "market_hash_name":"SG 553 | Army Sheen (Factory New)",
             "market_name":"SG 553 | Army Sheen (Factory New)",
             "name_color":"D2D2D2",
             "background_color":"",
             "type":"Consumer Grade Rifle",
             "tradable":1,
             "marketable":1,
             "commodity":0,
             "descriptions":[
                {
                   "type":"html",
                   "value":"Exterior: Factory New"
                },
                {
                   "type":"html",
                   "value":"The Bank Collection",
                   "color":"9da1a9",
                   "app_data":{
                      "def_index":"65535",
                      "is_itemset_name":1
                   }
                },
             ],
             "actions":[
                {
                   "name":"Inspect in Game...",
                   "link":"steam:\/\/rungame\/730\/76561202255233023\/+csgo_econ_action_preview%20S%owner_steamid%A%assetid%D2486209296654018845"
                }
             ],
             "market_actions":[
                {
                   "name":"Inspect in Game...",
                   "link":"steam:\/\/rungame\/730\/76561202255233023\/+csgo_econ_action_preview%20M%listingid%A%assetid%D2486209296654018845"
                }
             ],
             "tags":[
                {
                   "internal_name":"CSGO_Type_Rifle",
                   "name":"Rifle",
                   "category":"Type",
                   "category_name":"Type"
                },
                {
                   "internal_name":"weapon_sg556",
                   "name":"SG 553",
                   "category":"Weapon",
                   "category_name":"Weapon"
                },
             ]
          }
       }
    }
    

    完整的JSON响应:http://steamcommunity.com/id/Mambocsgoshack/inventory/json/730/2/

    我相信,因为广告资源中每个项目的标识符都在不断变化(例如469449975_0619638799_0),我必须将其反序列化为字典。

    到目前为止,这是我的代码:

    namespace SteamTrade
    {
        public class CSGOInventory
        {
            public static CSGOInventory FetchInventory(string steamId)
            {
                WebClient client = new WebClient();
                var url = "http://steamcommunity.com/profiles/" + steamId + "/inventory/json/730/2/";
                string response =  client.DownloadString(url);
                Dictionary<string, Item> result = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, Item>>(response);
                return new CSGOInventory(result);
            }
    
            public Item[] Items { get; set; }
            public bool IsPrivate { get; private set; }
            public bool IsGood { get; private set; }
    
            protected CSGOInventory(Dictionary<string, Item> apiInventory)
            {
                for (int i = 0; i < apiInventory.Count; i++)
                {
                    Items[i] = apiInventory.Values.ElementAt(i);
                }
            }
    
            /*public Item GetItem(int id)
            {
                return (Items == null ? null : Items.FirstOrDefault(item => item.instanceid == id));
            }
    
            public List<Item> GetItemsByDefindex(int defindex)
            {
                return Items.Where(item => item.def_index == defindex).ToList();
            }*/
    
            public class Item
            {
                public string AppId = "730";
                public string ContextId = "2";
    
                [JsonProperty("instanceid")]
                public string instanceid { get; set; }
    
                [JsonProperty("market_name")]
                public string market_name { get; set; }
    
                [JsonProperty("def_index")]
                public string def_index { get; set; }
    
            }
    
            protected class InventoryResult
            {
                public Item[] items { get; set; }
            }
    
            protected class InventoryResponse
            {
                public InventoryResult result;
            }
    
        }
    }
    

    我相信我将字典项添加到Items数组完全错误,但无法找出正确的解决方案。

    但是,目前的错误是:

      

    将值True转换为类型'SteamTrade.CSGOInventory + Item,路径'成功',第1行,第15位。

    我理解这意味着什么,但不知道如何解决它。我不认为我必须定义JSON在我的对象中返回的每个属性,但我可能错了。无论哪种方式,由于JSON的格式从rgInventory更改为rgDescriptions部分更改,我不知道如何解决此问题。谁能解释一下如何做到这一点?

    更新:

    从market_name检索instanceid的方法如下:

    public string getInstanceIdFromMarketName(string name)
            {
                var classIdToId = inventory.rgInventory.ToLookup(pair => pair.Value.classid, pair => pair.Key);
                var marketNameToId = inventory.rgDescriptions
                    .SelectMany(pair => classIdToId[pair.Value.classid].Select(id => new KeyValuePair<string, string>(pair.Value.market_name, id)))
                    .ToLookup(pair => pair.Key, pair => pair.Value);
                if (marketNameToId[name].First() != null)
                {
                    string idForMarket = marketNameToId[name].FirstOrDefault();
                    return idForMarket;
                }
                else
                {
                    return null;
                }
            }
    

    这会返回错误消息,说明序列中没有任何项目。

3 个答案:

答案 0 :(得分:2)

根据http://jsonformatter.curiousconcept.com/,您在问题中发布的JSON无效。但是,your link处的JSON是可以的,所以让我们继续。

您想要的信息可以建模为包含两个词典的类:

public class rgInventoryItem
{
    public string id { get; set; }
    public string classid { get; set; }
}

public class rgDescription
{
    public string classid { get; set; }
    public string market_name { get; set; }
}

public class InventoryResponse
{
    public Dictionary<string, rgInventoryItem> rgInventory { get; set; }

    public Dictionary<string, rgDescription> rgDescriptions { get; set; }
}

然后,要从JSON字符串加载,请使用:

    var response = JsonConvert.DeserializeObject<InventoryResponse>(json);

但是,rgDescriptions字典没有直接在classid上编入索引,而是以某种方式将密钥与classid相关联,例如

  "469449975_0":{
     "classid":"469449975",
     "market_name":"SG 553 | Army Sheen (Factory New)",

要从classid为市场名称创建lookup,您可以执行

    var classidToDescription = response.rgDescriptions.ToLookup(pair => pair.Value.classid);

这将找到给定rgDescription的所有classid个类。

如果您确定,给定rgDescription只有一个classid,您可以这样做:

    var classidToDescriptionDictionary = response.rgDescriptions.ToDictionary(pair => pair.Value.classid);

请注意,这会向ArgumentException发送消息&#34;已添加具有相同密钥的项目&#34;如果多个描述具有相同的类ID。

<强>更新

要从market_name转到id,您需要反转字典并创建反向查找表。因此:

  1. 如果您需要回复中的所有市场名称,请执行以下操作:

        var marketNames = response.rgDescriptions.Values.Select(d => d.market_name);
    
  2. 如果您需要响应中的所有ID,请执行以下操作:

        var ids = response.rgInventory.Keys;
    
  3. market_name映射到id,首先创建反向查找:

        var classIdToId = response.rgInventory.ToLookup(pair => pair.Value.classid, pair => pair.Key);
        var marketNameToId = response.rgDescriptions
            .SelectMany(pair => classIdToId[pair.Value.classid].Select(id => new KeyValuePair<string, string>(pair.Value.market_name, id)))
            .ToLookup(pair => pair.Key, pair => pair.Value);
    

    要获取引用给定市场名称的所有 ID,请执行以下操作:

        var idsForMarket = marketNameToId[name].ToList();
    

    要获取引用给定市场名称的第一个 ID,请执行以下操作:

        var firstIdForMarket = marketNameToId[name].FirstOrDefault();
    
  4. 更新2

    以下是您的课程的修改版本:

    public class rgInventoryItem
    {
        public string id { get; set; }
        public string classid { get; set; }
    }
    
    public class rgDescription
    {
        public string classid { get; set; }
        public string market_name { get; set; }
    }
    
    public class CSGOInventory
    {
        public static CSGOInventory FetchInventory(string steamId)
        {
            var url = "http://steamcommunity.com/profiles/" + steamId + "/inventory/json/730/2/";
            return FetchInventoryFromUrl(new Uri(url));
        }
    
        public static CSGOInventory FetchInventoryFromUrl(Uri url)
        {
            using (WebClient client = new WebClient())
            {
                string response = client.DownloadString(url);
                var inventory = JsonConvert.DeserializeObject<InventoryResponse>(response);
                return new CSGOInventory(inventory);
            }
        }
    
        readonly InventoryResponse inventory;
        readonly ILookup<string, string> classIdToId;
        readonly ILookup<string, string> marketNameToId;
    
        CSGOInventory(InventoryResponse inventory)
        {
            if (inventory == null)
                throw new ArgumentNullException();
    
            this.inventory = inventory;
    
            this.classIdToId = inventory.rgInventory.ToLookup(pair => pair.Value.classid, pair => pair.Key);
            this.marketNameToId = inventory.rgDescriptions
                .SelectMany(pair => classIdToId[pair.Value.classid].Select(id => new KeyValuePair<string, string>(pair.Value.market_name, id)))
                .ToLookup(pair => pair.Key, pair => pair.Value);
        }
    
    
        public IDictionary<string, rgInventoryItem> InventoryItems { get { return this.inventory == null ? null : this.inventory.rgInventory; } }
    
        public IDictionary<string, rgDescription> InventoryDescriptions { get { return this.inventory == null ? null : this.inventory.rgDescriptions; } }
    
        public IEnumerable<string> MarketNames { get { return InventoryDescriptions == null ? null : InventoryDescriptions.Values.Select(d => d.market_name); } }
    
        public IEnumerable<string> InventoryIds { get { return InventoryItems == null ? null : InventoryItems.Keys; } }
    
        public string getInstanceIdFromMarketName(string name)
        {
            return marketNameToId[name].FirstOrDefault();
        }
    
        public IEnumerable<string> getInstanceIdsFromMarketName(string name)
        {
            return marketNameToId[name];
        }
    
        class InventoryResponse
        {
            public Dictionary<string, rgInventoryItem> rgInventory { get; set; }
    
            public Dictionary<string, rgDescription> rgDescriptions { get; set; }
        }
    }
    

    使用它,以下测试类:

    public static class TestClass
    {
        public static void Test()
        {
            //string url = @"d:\temp\question28328432.json";
            string url = @"http://steamcommunity.com/id/Mambocsgoshack/inventory/json/730/2/";
            var inventory = CSGOInventory.FetchInventoryFromUrl(new Uri(url));
            foreach (var market in inventory.MarketNames)
            {
                Console.WriteLine(string.Format("    Market {0,-50}: id {1}", market, inventory.getInstanceIdFromMarketName(market)));
            }
        }
    }
    

    给出输出

    Market SG 553 | Army Sheen (Factory New)                 : id 1482735510
    Market Offer | Music Kit | Noisia, Sharpened             : id 1468698711
    Market Offer | Sticker | Bomb Squad (Foil)               : id 1468698710
    Market Offer | Sticker | Dinked                          : id 1468698709
    Market Offer | Sticker | Kawaii Killer CT                : id 1468698708
    Market Operation Breakout Weapon Case                    : id 1462270322
    Market Operation Vanguard Weapon Case                    : id 1459818809
    Market M4A4 | Howl (Minimal Wear)                        : id 1450750270
    Market Operation Phoenix Weapon Case                     : id 1391297747
    Market Negev | Army Sheen (Minimal Wear)                 : id 1370560151
    Market Huntsman Weapon Case                              : id 1305163655
    Market Tec-9 | Army Mesh (Minimal Wear)                  : id 1304896559
    Market Galil AR | Cerberus (Well-Worn)                   : id 1214784536
    Market StatTrakT Tec-9 | Sandstorm (Field-Tested)        : id 1201208194
    Market G3SG1 | Contractor (Field-Tested)                 : id 1189828757
    Market Campaign Vanguard                                 : id 1103736871
    Market Campaign Weapons Specialist                       : id 1103736870
    Market Operation Vanguard Challenge Coin                 : id 1103736869
    Market StatTrakT XM1014 | Red Python (Field-Tested)      : id 957595359
    Market StatTrakT CZ75-Auto | Hexane (Field-Tested)       : id 814442137
    Market Negev | Army Sheen (Factory New)                  : id 623936007
    Market SSG 08 | Sand Dune (Well-Worn)                    : id 616381102
    Market Silver Operation Breakout Coin                    : id 612997861
    Market UMP-45 | Scorched (Field-Tested)                  : id 603041123
    

答案 1 :(得分:1)

您要做的是查询您的JSON。为此,您可以使用DixonD提议的动态对象对其进行反序列化,然后遍历动态对象以查找所需的信息。

另一个更简单,更简洁的解决方案是使用像jsonpath这样的库来查询您的JSON,这类似于XPath for JSON。有关如何执行此类操作的示例here

答案 2 :(得分:0)

您正在尝试将其反序列化为Dictionary<string, Item>,这显然会失败,因为您的元素无法反序列化为Item,并且在第一个"success": true上失败

您有几种方法可以继续。

  1. var result = (dynamic)Newtonsoft.Json.JsonConvert.DeserializeObject(response);

    在这种情况下,您可以将结果作为动态对象处理。

  2. 定义并使用与您尝试反序列化的json格式相对应的类。