0

Note: re: related questions - they have similar titles but their titles are misleading: reading them reveals they're about a different situation, they're worried about INCOMING null, whereas I need a null but don't have one.

TL:DR: why is Json generating an error when no error should be generated? Why is it not accepting the null output? Why does it work when the input is null and the output is null - but not when the input is non-null and the output is null?

I have a custom JsonConverter that interprets a json object that is a polymorphic class, and generates the correct subclass based on a child property.

public class Metadata // base class
public class MetadataText : Metadata
public class MetadataImage : Metadata
... etc

Each class with a child Metadata property has it marked as nullable:

public class Author
{
   [CanBeNull] public Metadata metadata { get; set; }
}

This works fine for many variations of "metadata" in the incoming JSON. However... I have no control over the incoming json - huge files from a third party - and annoyingly they sometimes return "{}" where they should have returned "null".

i.e.

...
"author":{
 "metadata":
  {}
 }

...which SHOULD BE:

"author":{
 "metadata": null
 }

This seems to be generating a huge number of fake errors on deserialization (currently captured and logged to console using a custom error-handler)

var jsonSettings = new JsonSerializerSettings()
        {
            NullValueHandling = NullValueHandling.Ignore,
            MissingMemberHandling = MissingMemberHandling.Ignore,
            Error = HandleDeserializationError // just outputs the error
 };

I can't seem to find a way to get rid of those fake errors:

Value cannot be null.
Parameter name: value, path = [0].[OMITTED].author.metadata
class MetadataConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(Metadata).IsAssignableFrom(objectType);
    }

    // has no effect: [CanBeNull]
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject item = JObject.Load(reader);

        if( item.Count == 0 )
        {
            Debug.Log( "Found a Metadata with count = 0; "+reader.TokenType+", "+reader.Path+", "+reader.Value+" -- item: "+item.Type+", "+item.Parent?.Parent+", "+item.Path+", "+item.Properties().Count() );
            return (Metadata)null; // should be OK!
        }
        
        switch (item["command"].Value<string>())
        {
            case "open_url":    return item.ToObject<Metadata_OpenURL>();
            case "search":  return item.ToObject<Metadata_Search>();
            case "mclick":  return item.ToObject<Metadata_MClick>();
            case "msearch": return item.ToObject<Metadata_MSearch>();
            default: return item.ToObject<Metadata>();
        }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}
7
  • What exactly is your question? Commented May 29, 2024 at 12:37
  • Does this answer your question? Handling null objects in custom JsonConverter's ReadJson method Commented May 29, 2024 at 13:06
  • @PeterCsala updated the question to be more explicit - I don't know why Json.net is generating these errors in this specific situation, an, and json.net provides no extra information in the error object to search on or investigate, so I can only guess at the variations on what is wrong here :( -- as per the question body: I've checked all the things that should be in place, they are in place, but ... they aren't working. Commented May 29, 2024 at 13:26
  • @erisco thanks, updated question to be explicit - that other question deals with an unexpected null input. I have the opposite (?) problem: expected null input but unable to correct it into a null output. Commented May 29, 2024 at 13:27
  • The question's code doesn't apply the converter anywhere. Have you tried with [JsonConverter(typeof(MetadataConverter))] public Metadata? Metadata {get;set;} ? CanBeNull doesn't affect deserialization, it affects the compiler's nullability checks. An empty Dictionary/object isn't null anyway. If you expect Metadata to be null, the property should be Metadata?. Commented May 29, 2024 at 13:37

1 Answer 1

1

Ah, trial and error, figured it out.

  1. JsonConverter.ReadJson is potentially called multiple times for the same object; my assumption that it would be called precisely once misled me
  2. JSON.net's error handling is incorrect and sends unwrapped exceptions where it should be sending wrapped ones.

Net effect: the error was being generated in the line of code that came AFTER the "return" statement. That should be impossible - but note point 1 above: first time around it was hitting the return, second time around it was bypassing it and then hitting an error in the next line.

The clue is that if Json.net returns an error that is curiously missing in things like the "OriginalObject" (i.e. all the fields of the ErrorArgs are blank) ... you've run into Json.net's own error handling whereby instead of wrapping nested exceptions it just ... ignores them, and complains later.

In these cases, it means some other piece of code (invoked as a side-effect of the json.net deserialization) is crashing, and the raw exception is being bubbled through unwrapped.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.