0

I have the following json being returned by a third party api

{
    'category':'Cat1',
    'date': '12-12-2021'
    'amount' 23.23
}

which is being deserialized to following C# class

public class Quote{
    public string category {get;set;}
    public DateTime date {get;set;}
    public decimal amount {get;set;}
}

but sometimes we get None as value, eg:

{
    'category': None,
    'date': None
    'amount' None
}

and the application errors out while deserializing, actually it errors out while parsing the json before even attempting to serialize.

 JsonConvert.DeserializeObject<Quote>(json, settings);<--exception here

'Error parsing NaN value. Path 'category', line X, position XX.'

We tried replacing None with null using json.Replace("None", "null");, but then we get an exception when it tries to set date & amount, which is a DateTime and decimal type respectively, to null.

Is there a way to check the incoming value, and whenever we get None, we set the property with default(Type) value either globally in JsonSerializerSettings or using individually using custom attribute?

j4rey
  • 2,582
  • 20
  • 34
  • 1
    So what you are actually saying is you are getting invalid JSON - your first example is actually also invalid - well, they both are really – Mark Schultheiss Dec 02 '21 at 17:17
  • first JSON is fine....I didn't copy paste the actual json............the problem is the `None` which is equivalent to C# `null` or `default(Type)` where a type cannot be `null`. – j4rey Dec 02 '21 at 19:54
  • You could look into custom deserialize that checks for "None" and makes it null instead? https://www.newtonsoft.com/json/help/html/CustomJsonConverterGeneric.htm – Nikki9696 Dec 03 '21 at 00:11

1 Answers1

1

This is really about validation - which you should do before consuming anything from an external source.

This is invalid JSON:

{
    'category':'Cat1',
    'date': '12-12-2021'
    'amount' 23.23
}

{
    'category': Done,
    'date': Done
    'amount' Done
}

It should be this format

  • Double quotes not single
  • no missing ":" between the name/value pair
  • No missing comma between except the last one should have no comma after it

Example of allowed:

{
    "category":'Cat1",
    "date": "12-12-2021",
    "amount": 23.23
}

Example of allowed:

{
    "category": "None",
    "date": "None",
    "amount": "None"
}

You COULD use a valid null value set of pairs in the JSON string:

{
    "category": null,
    "date": null,
    "amount": null
}

Here is an example c# class with nullable value types:

public class Quote
{
    public string category {get;set;}
    public DateTime? date {get;set;}
    public decimal? amount {get;set;}
}

Now as for the actual validation and what gets replaced with what, that could be on the method accepting the values or in the setter for Quote. Rather than repeat a lot of code here I reference other questions with answers for that since that is a architectural and design issue:

Put validation in the setter: C# add validation on a setter method

Now if you want to validate before/outside the setter, you can do something like this very rough outline: How to handle exceptions is up to you.

public class TestQuote
{
    // these COULD be nullable types (see above) or some default
    public string Category { get; private set; }
    public DateTime? QuoteDate { get; private set; }
    public decimal? Amount { get; private set; }

    private NumberStyles style;

    public void SetCategory(string value)
    {
         //... your logic, throw exception if validation failed
         Category = value;
    }

    public void SetAmount(string value)
    {
         //... your logic, throw exception if validation failed
         // Now how you parse and default value is up to you re:
         // https://learn.microsoft.com/en-us/dotnet/api/system.decimal.parse?view=net-6.0
         style = NumberStyles.Number | NumberStyles.AllowCurrencySymbol;
         Amount = Decimal.Parse(value, style, CultureInfo.InvariantCulture);
    }
    
    public void SetQuoteDate(string value)
    {
         //... your logic, throw exception if validation failed 
         // OR change to some default DateTime
         // HOW you parse this and what default is up to you ref:
         // https://learn.microsoft.com/en-us/dotnet/api/system.datetime.parse?view=net-6.0
         QuoteDate = DateTime.Parse(value);
    }
}

Now how you call that is up to you since you provided no method that accesses the source of the JSON.

Mark Schultheiss
  • 32,614
  • 12
  • 69
  • 100