我将如何使用字典解析此 JSON?

How would I parse this JSON using a dictionary?

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

  1. 能够从rgInventory部分检索到id的搜索,然后得到对应的classid
  2. 通过 classid
  3. 获取对象的 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 数组中完全错误,但无法找出正确的解决方案。

但是,目前的错误是:

Error converting value True to type 'SteamTrade.CSGOInventory+Item , Path 'success', line 1, position 15.

我有点理解这意味着什么,但不知道如何解决它。我不认为我必须在我的对象中定义 JSON returns 的每个 属性,但我很可能是错的。无论哪种方式,由于 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;
            }
        }

这个 returns 一个错误,指出序列中没有项目。

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

您有几种方法可以继续。

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

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

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

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

另一个更简单、更清晰的解决方案是使用 jsonpath which is the equivalent of XPath for JSON. An example of how to do such thing is available here.

之类的库查询 JSON

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

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

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

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

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

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

请注意,如果多个描述具有相同的 class ID,这将抛出 ArgumentException 消息 "An item with the same key has already been added"。

更新

为了从 market_nameid,您需要反转字典并创建反向查找表。因此:

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

    要获取引用给定市场名称的 first id,请执行:

        var firstIdForMarket = marketNameToId[name].FirstOrDefault();
    

更新 2

这是您的 classes:

的修改版本
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; }
    }
}

使用这个,下面的测试class:

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