2015-02-04 4 views
1

Я делаю программу, которая должна анализировать ответ JSON. Большинство данных в ответе бесполезно, но мне нужно сделать несколько вещей с ним.Как бы разобрать этот JSON с помощью словаря?

  1. Уметь извлекать поиски id из раздела rgInventory, а затем получить соответствующий classid
  2. Получить market_name для объекта с этим classid

JSON Response (фрагмент кода, он по-прежнему с таким форматом):

{ 
    "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/

Я считаю, что идентификаторы продолжают меняться для каждого элемента в инвентаре (т. до 619638799_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 совершенно неправильно, но не могу понять, правильное решение.

Однако ошибка в настоящее время является:

Ошибка преобразования значения Верная типа «SteamTrade.CSGOInventory + пункт, путь 'успех', строка 1, позиция 15.

I рода понимайте, что это значит, но не знаете, как обойти это. Я не думал, что мне нужно определить каждое свойство, которое JSON возвращает в моем объекте, но я вполне мог ошибаться. В любом случае, поскольку формат JSON изменяется от rgInventory до rgDescriptions изменений раздела, я не знаю, как это решить. Может ли кто-нибудь объяснить, как это сделать?

UPDATE:

Мой метод для извлечения InstanceID из market_name выглядит следующим образом:

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

Это возвращает ошибку, говорящую нет элементов в последовательности.

ответ

2

JSON, отправленный в ваш вопрос, недействителен, согласно http://jsonformatter.curiousconcept.com/. Тем не менее, JSON на your link в порядке, так что давайте с этим справимся.

Информация, которую вы хотите, может быть смоделирован как класс, содержащий два словаря:

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)", 

Чтобы создать lookup для названия рынка от ClassID, вы можете сделать

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

Найдет все rgDescription классы для данного classid.

Если вы уверены есть только один rgDescription для данного classid, вы можете сделать:

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

Имейте в виду, это будет сгенерировано ArgumentException с сообщением «элемент с тем же ключом уже был добавлен ", если более одного описания имеют одинаковый идентификатор класса.

обновление

Для того, чтобы перейти от market_name к id, вам необходимо инвертировать словари и создавать обратные таблицы поиска.Таким образом:

  1. Если вам нужны все имена рынка в ответ, сделайте следующее:

    var marketNames = response.rgDescriptions.Values.Select(d => d.market_name); 
    
  2. Если вам нужны все идентификаторы в ответ, сделайте следующее:

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

    Чтобы получить все идентификаторов, что относится к данному имени рынка, сделайте следующее:

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

    Чтобы получить первый идентификатор, который относится к данному имени рынка, сделайте следующее:

    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:\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 
+0

Так что, учитывая название рынка, я мог бы получить, чтобы classid делал это в обратном порядке эффективно? –

+0

Вы хотите знать, как я создал модель данных, как использовать работу с полем 'rgInventory', или как использовать возвращаемое значение из' response.rgDescriptions.ToLookup'? – dbc

+0

плохой вопрос на мой конец. Мне нужно создать метод, который может получить «id» в разделе rgInventory с market_name –

0

Вы пытаетесь десериализации его Dictionary<string, Item>, который, очевидно, терпит неудачу, так как у вас есть элементы, которые не могут быть десериализованными к Item и он терпит неудачу на первое из них, который "success": true

У вас есть несколько способов, как вы можете продолжить ,

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

    В таком случае вы можете обработать результат в виде динамического объекта.

  2. Определите и используйте класс, соответствующий формату json, который вы пытаетесь десериализовать.

+0

сказать, что я использовать первый метод, предложенный, как я могу перейти к получению необходимой мне функциональности? (указано в верхней части моего сообщения) –

1

То, что вы пытаетесь сделать, действительно запрашивает ваш JSON. Для этого вы можете десериализовать его с помощью динамического объекта, такого как DixonD, и затем выполнить итерацию через свой динамический объект, чтобы найти нужную вам информацию.

Другим более простым и понятным решением является запрос вашего JSON, используя библиотеку, такую ​​как jsonpath, что эквивалентно XPath для JSON. Пример того, как это сделать, доступен here.