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



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

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

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"时出错,路径"success",第1行,位置15。

我有点理解这意味着什么,但不知道如何解决它。我不认为我必须定义JSON在我的对象中返回的每个属性,但我很可能错了。不管怎样,由于JSON的格式从rgInventoryrgDescriptions部分都发生了变化,我不知道如何解决这个问题。有人能解释一下怎么做吗?

更新:

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

这将返回一个错误,表示序列中没有项目。

根据http://jsonformatter.curiousconcept.com/.但是,链接处的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创建市场名称查找,可以执行

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

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

如果确定给定的classid只有一个rgDescription,则可以执行:

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

请注意,如果多个描述具有相同的类id,这将抛出一个ArgumentException,并显示消息"已添加具有相同密钥的项目"。

更新

为了从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();
    

更新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:tempquestion28328432.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

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

另一个更简单、更干净的解决方案是使用类似jsonpath的库来查询JSON,jsonpath相当于JSON的XPath。这里有一个如何做这件事的例子。

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

你有几种方法可以继续。

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

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

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

相关内容

  • 没有找到相关文章

最新更新