【问题标题】:How to check if a property exists before trying to add to list如何在尝试添加到列表之前检查属性是否存在
【发布时间】:2020-03-21 07:08:53
【问题描述】:

我目前正在从 Bungie API Manifest 中提取数据。我成功检索了 .json 文件,我正处于尝试从文件中提取我想要的内容的阶段。

我正在尝试遍历 JSON 文件中的所有条目并添加到 activity.Keyactivity.displayProperties.name 都存在的列表中,如下例所示

"2693136600": {
    "displayProperties": {
    "description": "\"Grow fat from strength.\"",
    "name": "Leviathan: Normal",
    "icon": "/common/destiny2_content/icons/8b1bfd1c1ce1cab51d23c78235a6e067.png",
    "hasIcon": true
    },

对上述特定 JSON 密钥的完整响应:

"2693136600": { 
"displayProperties": { 
"description": "\"Grow fat from strength.\"", 
"name": "Leviathan: Normal", 
"icon": "/common/destiny2_content/icons/8b1bfd1c1ce1cab51d23c78235a6e067.png", 
"hasIcon": true 
}, 
"originalDisplayProperties": { 
"description": "\"Grow fat from strength.\"", 
"name": "Leviathan", 
"icon": "/img/misc/missing_icon_d2.png", 
"hasIcon": false 
}, 
"selectionScreenDisplayProperties": { 
"description": "Accept an invitation from the Cabal emperor to prove yourself aboard the Leviathan.\n\nRaids are 6 player cooperative activities which test the limits of your skill and teamwork. Master the unique mechanics of each encounter to succeed and earn powerful and exclusive rewards.", 
"name": "Normal", 
"hasIcon": false 
}, 
"releaseIcon": "/img/misc/missing_icon_d2.png", 
"releaseTime": 0, 
"activityLevel": 30, 
"completionUnlockHash": 0, 
"activityLightLevel": 750, 
"destinationHash": 3093898507, 
"placeHash": 330251492, 
"activityTypeHash": 2043403989, 
"tier": 0, 
"pgcrImage": "/img/destiny_content/pgcr/raid_gluttony.jpg", 
"rewards": [ 
{ 
"rewardItems": [ 
{ 
"itemHash": 2127149322, 
"quantity": 0 
} 
] 
}, 
{ 
"rewardItems": [ 
{ 
"itemHash": 669267835, 
"quantity": 0 
} 
] 
} 
], 
"modifiers": [ 
{ 
"activityModifierHash": 2863316929 
}, 
{ 
"activityModifierHash": 3296085675 
}, 
{ 
"activityModifierHash": 871205855 
}, 
{ 
"activityModifierHash": 2770077977 
} 
], 
"isPlaylist": false, 
"challenges": [], 
"optionalUnlockStrings": [], 
"inheritFromFreeRoam": false, 
"suppressOtherRewards": false, 
"playlistItems": [], 
"matchmaking": { 
"isMatchmade": false, 
"minParty": 1, 
"maxParty": 6, 
"maxPlayers": 6, 
"requiresGuardianOath": false 
}, 
"directActivityModeHash": 2043403989, 
"directActivityModeType": 4, 
"activityModeHashes": [ 
2043403989, 
1164760493 
], 
"activityModeTypes": [ 
4, 
7 
], 
"isPvP": false, 
"insertionPoints": [ 
{ 
"phaseHash": 2188993306, 
"unlockHash": 0 
}, 
{ 
"phaseHash": 1431486395, 
"unlockHash": 0 
}, 
{ 
"phaseHash": 3847906370, 
"unlockHash": 0 
}, 
{ 
"phaseHash": 4231923662, 
"unlockHash": 0 
} 
], 
"activityLocationMappings": [], 
"hash": 2693136600, 
"index": 559, 
"redacted": false, 
"blacklisted": false 
},

我的问题是displayProperties 并不总是包含name,所以一旦发生这种情况,代码就会失败。

我已经尝试了各种方法来做这件事,因为它们都是catch

我对这个不够了解,但它几乎是我正在构建的拼图中的最后一块,所以任何帮助都将不胜感激。

以下是我最近失败的尝试

HttpResponseMessage response = await client.GetAsync("https://bungie.net/common/destiny2_content/json/en/DestinyActivityDefinition-7ab91c74-e8a4-40c7-9f70-16a4354125c0.json");

    if (response.IsSuccessStatusCode)
    {
       try
       {
           dynamic content = response.Content.ReadAsAsync<ExpandoObject>().Result;                   

           foreach (dynamic activity in content)
           {
           //check if .name field exists under displayProperties
               bool exist = activity.displayProperties.name;
               if (exist == false)
               {
               //do nothing
               }
               else
               {
               //add both Key and displayProperties.name to list
                   bungieActivities.Add(new AllBungieActivities() { ActivityHash = activity.Key, ActivityDescription = activity.displayProperties.name });
               }                        
            }  
         }
         catch
         {
           throw new ArgumentException("The member could not be found.");
         }
      }

更新 我没有设法实现以下任何答案并让它工作,但是我有经理让下面工作但需要相当多的时间才能通过这部分

List<AllBungieActivities> bungieActivities = new List<AllBungieActivities>();

            var response = await client.GetAsync("https://bungie.net/common/destiny2_content/json/en/DestinyActivityDefinition-7ab91c74-e8a4-40c7-9f70-16a4354125c0.json");

            if (response.IsSuccessStatusCode)
            {
                try
                {
                    var content = await response.Content.ReadAsStringAsync();
                    JObject activity = JObject.Parse(content);

                    foreach (JObject refId in activity.Properties().Select(p => p.Value["displayProperties"]))
                    {
                        if (refId.ContainsKey("name"))
                        {
                            bungieActivities.Add(new AllBungieActivities() { ActivityName = (string)refId["name"], ActivityHash = refId.Path.Substring(0, refId.Path.LastIndexOf(".") + 0) });
                        }
                    }

                }
                catch
                {
                    throw new ArgumentException("The member could not be found.");
                }
            }

public class AllBungieActivities
    {
        public string ActivityName;
        public string ActivityHash;
    }

【问题讨论】:

标签: c# json


【解决方案1】:

不要反序列化为ExpandoObject,而是创建一个代表您的数据的实际模型并使用它。如果您的 json 中没有必要的键,您的模型将为空,然后您可以进行空检查。

public class Activity
{
    public string Key {get;set;}
    public DisplayProperties DisplayProperties {get;set;}
}

public class DisplayProperties
{
   public string Description {get;set;}
   public string Name {get;set;}
   public string Icon {get;set;}
   public bool HasIcon {get;set;}
}


var activity= response.Content.ReadAsAsync<Activity>().Result;

if(!string.IsNullOrWhiteSpace(activity.Key) && !string.IsNullOrWhiteSpace(activity.DisplayProperties?.Name)
//add to list

【讨论】:

  • 这里的巨大好处当然是强类型和编译时安全。
  • 谢谢,这似乎是最明智的做法。然而,我面临两个问题。 1:它总是返回 Null,它不允许我使用 foreach 语句,因为 Activity 类中没有公共 IEnumarable
  • @Danny 您非常短的 json sn-p 不足以确定整个模型。请更新您的问题以显示可行的最小 json。
  • @JohanP - 如果您还需要什么,请告诉我
  • @Danny 请用内容更新您的问题,而不是外部链接。
【解决方案2】:

您可以将 ExpandoObject 转换为字典:

HttpResponseMessage response = await client.GetAsync("https://bungie.net/common/destiny2_content/json/en/DestinyActivityDefinition-7ab91c74-e8a4-40c7-9f70-16a4354125c0.json");

if (response.IsSuccessStatusCode)
{
   try
   {
       dynamic content = response.Content.ReadAsAsync<ExpandoObject>().Result;                   

       foreach (dynamic activity in content)
       {
           var dict = ((IDictionary<String, object>)activity.displayProperties)
           //check if .name field exists under displayProperties
           bool exist = dict.ContainsKey("name");
           if (exist == false)
           {
           //do nothing
           }
           else
           {
           //add both Key and displayProperties.name to list
               bungieActivities.Add(new AllBungieActivities() { ActivityHash = activity.Key, ActivityDescription = activity.displayProperties.name });
           }                        
        }  
     }
     catch
     {
       throw new ArgumentException("The member could not be found.");
     }
  }

【讨论】:

    【解决方案3】:

    StackOverflow 上有类似的问题。这是我找到的更好的答案之一:https://stackoverflow.com/a/2839629/12431728

    根据那个答案(参考 MSDN),您可以使用一个 ExpandoObject,它实现了 IDictionary:

    public sealed class ExpandoObject : IDynamicMetaObjectProvider, 
        IDictionary<string, Object>, ICollection<KeyValuePair<string, Object>>, 
        IEnumerable<KeyValuePair<string, Object>>, IEnumerable, INotifyPropertyChanged
    

    这个答案的建议解决方案:

    var expandoObject = ...;
    if(((IDictionary<String, object>)expandoObject).ContainsKey("SomeMember")) {
        // expandoObject.SomeMember exists.
    }
    

    您可以在此处找到有关 ExpandoObject 的更多信息:https://docs.microsoft.com/en-us/dotnet/api/system.dynamic.expandoobject?view=netframework-4.8

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-22
      • 2013-06-14
      • 1970-01-01
      • 2011-09-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多