I am calling contacts from the LightSpeed API.
When i call for the details of client "A" the JSON contains the following for his related email(s)
{
"Emails": {
"ContactEmail": {
"address": "clienta@yahoo.com",
"useType": "Primary"
}
}
}
When I call for the details of client "B" the JSON contains the following for this related email(s)
{
"Emails": {
"ContactEmail": [{
"address": "clientb1@gmail.com",
"useType": "Primary"
}, {
"address": "clientb2@gmail.com",
"useType": "Secondary"
}
]
}
}
If I am correct I believe that the first response should be an array even if there is only 1 "email" returned...? because the system does allow for customers to have more than 1 email in their record.
Here is the class I am trying to Deserialize into. It works perfectly for client "B" but fails for client "A"
public class GetCustomersResponse
{
public Attributes attributes { get; set; }
public List<Customer> Customer { get; set; }
}
public class Attributes
{
public string count { get; set; }
}
public class Customer
{
public string customerID { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
public string title { get; set; }
public string company { get; set; }
public string companyRegistrationNumber { get; set; }
public string vatNumber { get; set; }
public DateTime createTime { get; set; }
public DateTime timeStamp { get; set; }
public string archived { get; set; }
public string contactID { get; set; }
public string creditAccountID { get; set; }
public string customerTypeID { get; set; }
public string discountID { get; set; }
public string taxCategoryID { get; set; }
public Contact Contact { get; set; }
}
public class Contact
{
public string contactID { get; set; }
public string custom { get; set; }
public string noEmail { get; set; }
public string noPhone { get; set; }
public string noMail { get; set; }
public Addresses Addresses { get; set; }
public Phones Phones { get; set; }
public Emails Emails { get; set; }
public string Websites { get; set; }
public DateTime timeStamp { get; set; }
}
public class Addresses
{
public Contactaddress ContactAddress { get; set; }
}
public class Contactaddress
{
public string address1 { get; set; }
public string address2 { get; set; }
public string city { get; set; }
public string state { get; set; }
public string zip { get; set; }
public string country { get; set; }
public string countryCode { get; set; }
public string stateCode { get; set; }
}
public class Phones
{
public List<Contactphone> ContactPhone { get; set; }
}
public class Contactphone
{
public string number { get; set; }
public string useType { get; set; }
}
public class Emails
{
public List<Contactemail> ContactEmail { get; set; }
}
public class Contactemail
{
public string address { get; set; }
public string useType { get; set; }
}
I can't see me getting LightSpeed to change their API so can anyone suggest how to get the client with 1 email address to work with my class?
Any help would be greatly appreciated.
UPDATE:
with the help given I have got very close to some working code.
this is what I have for the custom json convertor:
public class ContactEmailJsonConverter : JsonConverter<List<ContactEmail>>
{
public override List<ContactEmail> Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
try
{
if (reader.TokenType == JsonTokenType.StartArray)
{
return (List<ContactEmail>)JsonSerializer
.Deserialize(ref reader, typeToConvert, options);
}
else if (reader.TokenType == JsonTokenType.StartObject)
{
var email = (ContactEmail)JsonSerializer
.Deserialize(ref reader, typeof(ContactEmail), options);
return new List<ContactEmail>(capacity: 1) { email };
}
else
{
throw new InvalidOperationException($"got: {reader.TokenType}");
}
}
catch(Exception ex)
{
return null;
}
}
public override void Write(Utf8JsonWriter writer, List<ContactEmail> value, JsonSerializerOptions options)
{
if ((value is null) || (value.Count == 0))
{
JsonSerializer.Serialize(writer, (ContactEmail)null, options);
}
else if (value.Count == 1)
{
JsonSerializer.Serialize(writer, value[0], options);
}
else
{
JsonSerializer.Serialize(writer, value, options);
}
}
}
But, I have now found a contact which appears not to have an email at all.. And the JSON returned by LightSpeed looks like this:
"Emails":""
and it's breaking the converter code I have written. I am not sure how to handle this completely empty object?
What you are going to have to do is create a couple custom JsonConverter
objects. Let's say your models look like this:
public class ContactEmail
{
[JsonPropertyName("address")]
public string Address { get; set; }
[JsonPropertyName("useType")]
public string UseType { get; set; }
}
public class Emails
{
public List<ContactEmail> ContactEmail { get; set; }
}
public class Root
{
public Emails Emails { get; set; }
}
First, you need to set up a custom converter to handle List<ContactEmail>
and the weird instances where it could be an array or single object:
public sealed class ContactEmailListJsonConverter
: JsonConverter<List<ContactEmail>>
{
public override List<ContactEmail> Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
if(reader.TokenType == JsonTokenType.StartArray)
{
return (List<ContactEmail>)JsonSerializer
.Deserialize(ref reader, typeToConvert, options);
}
else if (reader.TokenType == JsonTokenType.StartObject)
{
var email = (ContactEmail)JsonSerializer
.Deserialize(ref reader, typeof(ContactEmail), options);
return new List<ContactEmail>(capacity: 1) { email };
}
else
{
throw new InvalidOperationException($"got: {reader.TokenType}");
}
}
public override void Write(
Utf8JsonWriter writer,
List<ContactEmail> value,
JsonSerializerOptions options)
{
if((value is null) || (value.Count == 0))
{
JsonSerializer.Serialize(writer, (ContactEmail)null, options);
}
else if(value.Count == 1)
{
JsonSerializer.Serialize(writer, value[0], options);
}
else
{
JsonSerializer.Serialize(writer, value, options);
}
}
}
Second, you need to set up a custom converter to handle Emails
and the weird instances where it's not an actual Emails
object:
public sealed class EmailsJsonConverter : JsonConverter<Emails>
{
public override Emails Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
// MUST be an object!
if(reader.TokenType == JsonTokenType.StartObject)
{
return (Emails)JsonSerializer
.Deserialize(ref reader, typeToConvert, options);
}
// if it's not an object (ex: string), then just return null
return null;
}
public override void Write(
Utf8JsonWriter writer,
Emails value,
JsonSerializerOptions options)
{
JsonSerializer.Serialize(writer, value, options);
}
}
Then you'd add the converters to your models:
public class Emails
{
[JsonConverter(typeof(ContactEmailListJsonConverter))]
public List<ContactEmail> ContactEmail { get; set; }
}
public class Root
{
[JsonConverter(typeof(EmailsJsonConverter))]
public Emails Emails { get; set; }
}
And simply deserialize as normal:
var obj = JsonSerializer.Deserialize<Root>(jsonData);