2

So I'm working on a API wrapper with C#, I deserialize JSON data to a response model class using Newtonsoft.Json library. In the response model class, I have a list of sub-items, which each of contain a list of sub-items. These are defined like this:

public List<StatModel> Stats { get; set; }

each StatModel has a property which basically equals the name:

public Stat Stat { get; set; }

these are automatically deserialized, because each Stat is defined in an enum like this:

[EnumMember(Value = "Avg Walk Distance")]
AverageWalkDistance,

Now the problem is, if something changes in the actual API, the wrapper doesn't work since it doesn't have definition for the specified Stat. So this means if they add a new Stat to the API, the wrapper won't work until I manually add definition for it, like in the above code block.

So the question is, how can I ignore values that don't have corresponding Stat property available, or can I somehow re-design the whole thing so this doesn't happen? I'm guessing I have to define all new values by myself either way.

Here's the project on GitHub to get a better understanding of what I actually mean: https://github.com/eklypss/PUBGSharp

The Requester does the deserializing and returns a StatResponse, which has a list of sub-items called StatsRoot which each have their own list of StatModels which are the actual stat objects causing this issue. Each type of Stat is defined in the Enum/Stat.cs file.

dbc
  • 104,963
  • 20
  • 228
  • 340
ekl
  • 53
  • 1
  • 4
  • What JSON library are you using to deserialize? JSON.Net will ignore the properties that you don't have I believe. – Kasun Koswattha Jun 13 '17 at 17:38
  • Duplicate? [How can I ignore unknown enum values during json deserialization?](https://stackoverflow.com/q/22752075/3744182). – dbc Jun 13 '17 at 17:55
  • Yeah I am using Newtonsoft's Json.NET. The above question is indeed pretty much the same, wonder if there's other solutions than writing a custom JsonConverter though. – ekl Jun 13 '17 at 18:26
  • If you don't want to create your own converter you could use [exception handling](http://www.newtonsoft.com/json/help/html/serializationerrorhandling.htm). – dbc Jun 13 '17 at 18:53

3 Answers3

2

If you don't want to create your own tolerant version of StringEnumConverter, you could use Json.NET's exception handling features:

public class StatModel
{
    const string StatName = "label";

    [JsonProperty(StatName)]
    [JsonConverter(typeof(StringEnumConverter))]
    public Stat Stat { get; set; }

    public string Value { get; set; }
    public int? Rank { get; set; }
    public double? Percentile { get; set; }

    [OnError]
    void OnError(StreamingContext context, ErrorContext errorContext)
    {
        if (errorContext.OriginalObject == this && StatName.Equals(errorContext.Member))
        {
            errorContext.Handled = true;
        }
    }
}

When an exception is thrown while deserializing StatModel or any of its nested objects, its OnError() method will be called to possibly handle the error. The method checks to see whether the exception was thrown

  1. While deserializing this specific object, AND
  2. While deserializing the "label" member.

If so, the exception is swallowed. You could also take the opportunity to set a flag in the model indicating that the Stat property was invalid.

dbc
  • 104,963
  • 20
  • 228
  • 340
0

You can have json.net ignore missing members.

var settings = new JsonSerializerSettings
{
    NullValueHandling = NullValueHandling.Ignore,
    MissingMemberHandling = MissingMemberHandling.Ignore
};

var json = JsonConvert.DeserializeObject<MyClass>(jsonStr, settings);
Kyle
  • 5,407
  • 6
  • 32
  • 47
  • I actually tried this earlier. For some reason it has no effect at all, it still throws the same exception if something is missing. – ekl Jun 13 '17 at 18:19
  • 1
    [`MissingMemberHandling`](http://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_JsonSerializerSettings_MissingMemberHandling.htm) suppresses exceptions when the JSON *contains a property that isn't a member on the object*. It doesn't suppress exceptions on invalid *values* which is what is happening here. – dbc Jun 13 '17 at 19:17
0

Refer to the question below, this guy had same issues as you have.

How can I ignore unknown enum values during json deserialization?

However,

Even if you somehow manage to ignore new Stats, it's not good for your wrapper as consumers of your wrapper need to wait for you to add that new Enum.

So,

If I were you, I would think to change Stat from Enum to string, so whenever a new Stat comes, neither you need to change anything nor consumers of your wrapper, would not have to wait.

Community
  • 1
  • 1
Satish Yadav
  • 158
  • 3
  • 8
  • I was thinking about something like this, but the problem with this approach would be that the people using the wrapper would have to know the names of specific stats, instead of just using the values that are pre-defined in the Enum. So `Stats.Find(x => x.Stat == "Headshot Kills")` instead of just `(x => x.Stat == Stat.HeadshotKills)` – ekl Jun 14 '17 at 01:18