Mark Cheek
Mark Cheek

Reputation: 265

Json.net Deserialization Where properties may not exist

I'm trying to deserialize a JSON object that may or may not have items with certain attributes.

Here's an example, someone can have none or many phone numbers.

 "PhoneNumbers": [
  {
    "PhoneId": 190434,
    "PhoneNumber": "213-555-0001",
    "PhoneExtension": null,
    "PhoneType": {
      "@odata.type": "#Enterprise.Contracts.DataContracts.Party.PhoneType",
      "PhoneTypeId": 18,
      "PhoneTypeAbbreviation": null,
      "PhoneTypeCode": "MOBILE",
      "PhoneTypeName": "MOBILE PHONE",
      "PhoneTypeDescription": "MOBILE PHONE"
    }
  },
  {
    "PhoneId": 190436,
    "PhoneNumber": "213-555-0003",
    "PhoneExtension": null,
    "PhoneType": {
      "@odata.type": "#Enterprise.Contracts.DataContracts.Party.PhoneType",
      "PhoneTypeId": 16,
      "PhoneTypeAbbreviation": null,
      "PhoneTypeCode": "FAX",
      "PhoneTypeName": "FACSIMILE",
      "PhoneTypeDescription": "FACSIMILE"
    }
  },
  {
    "PhoneId": 190437,
    "PhoneNumber": "213-555-0004",
    "PhoneExtension": 50004,
    "PhoneType": {
      "@odata.type": "#Enterprise.Contracts.DataContracts.Party.PhoneType",
      "PhoneTypeId": 14,
      "PhoneTypeAbbreviation": null,
      "PhoneTypeCode": "OFFICE",
      "PhoneTypeName": "OFFICE PHONE",
      "PhoneTypeDescription": "OFFICE PHONE"
    }
  }
],

Some people instead of having a MOBILE PHONE may have a BLACKBERRY, and vise-versa. They could even have both. Here's how I'm trying to get those attributes.

if (item.PhoneNumbers != null)
{
    Output0Buffer.OfficePhone = item.PhoneNumbers.Where(p => p.PhoneType.PhoneTypeCode == "OFFICE").FirstOrDefault().PhoneNumber;
    Output0Buffer.MobilePhone = item.PhoneNumbers.Where(p => p.PhoneType.PhoneTypeCode == "MOBILE").FirstOrDefault().PhoneNumber;
    Output0Buffer.BlackBerry = item.PhoneNumbers.Where(p => p.PhoneType.PhoneTypeCode == "BLKBRRY").FirstOrDefault().PhoneNumber;
    Output0Buffer.Fax = item.PhoneNumbers.Where(p => p.PhoneType.PhoneTypeCode == "FAX").FirstOrDefault().PhoneNumber;
    Output0Buffer.Extension = item.PhoneNumbers.Where(p => p.PhoneType.PhoneTypeCode == "OFFICE").FirstOrDefault().PhoneExtension.ToString();
}

The problem I'm having is if someone doesn't have a Blackberry or they don't have a MOBILE PHONE, etc. I'm getting a null value exception.

I have my serializer settings to ignore null values, but I still seem to get the exception.

JsonSerializerSettings settings = new JsonSerializerSettings();
settings.NullValueHandling = NullValueHandling.Ignore;   
var results = JsonConvert.DeserializeObject<ODataResponse>(json,settings);

Any thoughts on how I can change that LINQ where clause to support this?

EDIT: This method here seems to work. I didn't know if there was a better way of handling this so I didn't have to wrap it around every attribute

if (item.PhoneNumbers != null)
{
    string officePhone = item.PhoneNumbers.Where(p => p.PhoneType.PhoneTypeCode == "OFFICE").Select(s => s.PhoneNumber).FirstOrDefault();
    if (officePhone != null)
        Output0Buffer.OfficePhone = officePhone;
    string mobilePhone = item.PhoneNumbers.Where(p => p.PhoneType.PhoneTypeCode == "MOBILE").Select(s => s.PhoneNumber).FirstOrDefault();
    if (mobilePhone != null)
        Output0Buffer.MobilePhone = mobilePhone;
    string blackBerry = item.PhoneNumbers.Where(p => p.PhoneType.PhoneTypeCode == "BLKBRRY").Select(s => s.PhoneNumber).FirstOrDefault();
    if (blackBerry != null)
        Output0Buffer.BlackBerry = blackBerry;
    string fax = item.PhoneNumbers.Where(p => p.PhoneType.PhoneTypeCode == "FAX").Select(s => s.PhoneNumber).FirstOrDefault();
    if (fax != null)
        Output0Buffer.Fax = fax;
    string extension = item.PhoneNumbers.Where(p => p.PhoneType.PhoneTypeCode == "OFFICE").Select(s => s.PhoneExtension).FirstOrDefault().ToString();
    if (extension != null)
        Output0Buffer.Extension = extension;

}

Upvotes: 1

Views: 1592

Answers (3)

Coaden
Coaden

Reputation: 466

I have used this WhereIf(bCondition, expression) extention method to accomplish what you're trying to do:

public static IEnumerable<TSource> WhereIf<TSource>(this IEnumerable<TSource> source, bool condition, Func<TSource, bool> predicate)
{
    if (condition)
        return source.Where(predicate);
    else
        return source;
}

public static IEnumerable<TSource> WhereIf<TSource>(this IEnumerable<TSource> source, bool condition, Func<TSource, int, bool> predicate)
{
    if (condition)
        return source.Where(predicate);
    else
        return source;
}

See this link for more information:

http://extensionmethod.net/csharp/ienumerable-t/whereif

Upvotes: 0

Brian Rogers
Brian Rogers

Reputation: 129827

FirstOrDefault() will return null if there is no item matching the Where clause. So if you try to deference that return value directly, then of course you will get an exception when it is null.

Try using Select after the Where to project to the result you want before using FirstOrDefault(), e.g.:

Output0Buffer.BlackBerry = item.PhoneNumbers
    .Where(p => p.PhoneType.PhoneTypeCode == "BLKBRRY")
    .Select(p => p.PhoneNumber)
    .FirstOrDefault();

Upvotes: 1

Anders Carstensen
Anders Carstensen

Reputation: 4194

The problem is that FirstOrDefault() may return null, so FirstOrDefault().PhoneNumber will throw an exception.

If you are using C# 4.6, you can do this (notice the ?):

Output0Buffer.OfficePhone = item.PhoneNumbers.Where(p => p.PhoneType.PhoneTypeCode == "OFFICE").FirstOrDefault()?.PhoneNumber;

Otherwise, you could do this instead:

    if (item.PhoneNumbers != null)
    {
        Output0Buffer.OfficePhone = item.PhoneNumbers.Where(p => p.PhoneType.PhoneTypeCode == "OFFICE").Select(p => p.PhoneNumber).FirstOrDefault();
        Output0Buffer.MobilePhone = item.PhoneNumbers.Where(p => p.PhoneType.PhoneTypeCode == "MOBILE").Select(p => p.PhoneNumber).FirstOrDefault();
        Output0Buffer.BlackBerry = item.PhoneNumbers.Where(p => p.PhoneType.PhoneTypeCode == "BLKBRRY").Select(p => p.PhoneNumber).FirstOrDefault();
        Output0Buffer.Fax = item.PhoneNumbers.Where(p => p.PhoneType.PhoneTypeCode == "FAX").Select(p => p.PhoneNumber).FirstOrDefault();
        Output0Buffer.Extension = item.PhoneNumbers.Where(p => p.PhoneType.PhoneTypeCode == "OFFICE").Select(p => p.PhoneExtension).FirstOrDefault();
    }

Upvotes: 0

Related Questions