0

I've written a custom JsonConverter which if it fails then attempts the JSON parse using the default behaviour. The code is below and it's working fine, inspired by this answer to another question.

Basically what I do is after my own attempt at conversion fails, I set the Converter property of the serialiser for my type to null, call the deserialisation (which seems to result in it skipping my converter, which is good) and then in my finally clause I restore it (I do this in the finally clause so the Converter will be restored even if an exception is called).

My questions are:

  1. Does the serialiser passed by JSON.Net to this function have a shared "ContractResolver" or it's own unique copy?
  2. If it has it's own unique copy, I presume the finally clause here is unnecessary yes?
  3. If it's "ContractResolver" is shared, I presume what I'm doing is horrifically non-thread safe, as any deserialisation from a separate thread which occurs at the same time may skip the custom serialiser if another deserialisation just happens to be going ahead at the same time (I presume I'll have to fix this with locks)?
  4. Is there a better totally different way to fix this?
public override object ReadJson (
    JsonReader reader,
    Type objectType,
    object existingValue,
    JsonSerializer serializer) 
    {
        JToken nextToken = JToken.ReadFrom (reader);
        try {
            using (JTokenReader reader1 = new JTokenReader (nextToken))
            {
                // Attempt to parse in custom fashion
            }
        }
        catch (Exception e)
        {
            // If the previous attempt threw, fallback behaviour here:
            using (JTokenReader reader2 = new JTokenReader(nextToken))
            {
                JsonConverter originalConverter = null;
                JsonContract contract = serializer.ContractResolver.ResolveContract(objectType);
                try
                {
                    originalConverter = contract.Converter;
                    contract.Converter = null;
                    return serializer.Deserialize(reader2, objectType);
                }
                finally
                {
                    contract.Converter = originalConverter;
                }
            }
        }        
    }
}

Edit:

Using a completely new serializer for the inner loop seems to also work and presumably avoids threading issues. Is this a reasonable approach?

using (JTokenReader reader2 = new JTokenReader(nextToken))
{
    JsonSerializer tempSerializer = JsonSerializer.Create(new JsonSerializerSettings());
    tempSerializer.ContractResolver.ResolveContract(objectType).Converter = null;
    return tempSerializer.Deserialize(reader2, objectType);
}
8
  • 1
    1, 3) The contract resolver is shared globally, see Does Json.NET cache types' serialization information?. 2) Even with a local contract resolver the finally is needed in case there are multiple non-nested instances of the type in the object graph being deserialized. 4) see JSON.Net throws StackOverflowException when using [JsonConvert()] and/or Json.NET custom serialization with JsonConverter - how to get the “default” behavior. Commented Mar 29, 2019 at 3:03
  • In fact this may be a duplicate of those three, or be distinct enough to warrant an answer. Do you have a preference? Commented Mar 29, 2019 at 3:03
  • @dbc: I think it might be somewhat distinct. See my edit. Is that a better approach? Commented Mar 29, 2019 at 3:15
  • Unfortunately the default contract resolver is still shared globally, see Does Json.NET cache types' serialization information?, so that will still cause problems. And if you allocate a local contract resolver for every call the performance will suffer. Disabling the converter using a [ThreadStatic] or using serializer.Populate() should work instead. Commented Mar 29, 2019 at 3:18
  • @dbc I can't see how [ThreadStatic] helps here, as I have to change the converter (i.e. set serializer.ContractResolver.ResolveContract(objectType).Converter = null) and that's going to be seen across all threads. I presume I'm missing something yes? Commented Mar 29, 2019 at 3:25

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.