Newtonsoft serialization of object with few not serializable properties

Viewed 48

I use the following code to serialize JSON

JsonSerializerSettings settings = new JsonSerializerSettings()
{
    Culture = CultureInfo.InvariantCulture,
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
    MaxDepth = 10
};
        
string serializationResult = JsonConvert.SerializeObject(currentObject, settings);

But i have found that if currentObject contains not serializable items it will throw Exception (for example a reference to dirty a DataContext). I have no control over currentObject because the actual code is part of a logging utility, and so 'currentObject' could be everything the utility user submitted to utility. I would like to be able to serialize 'currentObject' as much as i can, avoiding problematic properties if there are any.

So i modified the code in the following way :

    try
    {
        JsonSerializerSettings settings = new JsonSerializerSettings()
        {
            Culture = CultureInfo.InvariantCulture,
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            MaxDepth = 10,
            Error = delegate (object sender, ErrorEventArgs args)
            {
                args.ErrorContext.Handled = true;
            }
        };

        string serializationResult = JsonConvert.SerializeObject(currentObject, settings);

        return serializationResult;
    }
    catch ( Exception err)
    {
        return string.Empty;
    }

I test it again passing a 'currentObject' in which a property is a dirty DataConetxt, and now the serialization process never end. Seems like it keep looping on the error hanlder. After an huge amount of times (few minutes) i finally get a serialization error :

System.NotSupportedException: 'Specified method is not supported.'
   at System.Web.HttpResponseStream.get_Length()
   at GetLength(Object )
   at Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)

Is not clear to me what i am missin and how to handle such situation in which a property of object to serialize may be not serializable. Is my logic correct but the prolem is that DataContext is huge to serialize (and if so i have to try to filter out such situation)?
Or i mistakenly impelemented the error handling?

1 Answers

Even if not clear exactly where is the issue with DataContext, it's seems reasoneable to me that try to serialize such a complex object is not a good approach (unmanaged resource may be involved behind the hood).

As suggested by @Eidat i tried to create a test method to check if the serialization of an entry/property works in a given amount of time or if it fail. If the serailization failed then skip the entity/property.

This approach may be a bit too time consuming, but if performance is not a top prioritiy, or volume of data are low, this solution may be viable.

To do so in a resuable way i write that simple extension method on which i test the "critical" object before serialization (with critical i mean object i don't know where they came from, and for that reason they may be not serializable).

    /// <summary>
    /// Check if an object is serializable throught Newtonsoft serialization in a given amount of time
    /// </summary>
    /// <param name="this">The object to test</param>
    /// <param name="settings">The settings to use for serialization (optional)</param>
    /// <param name="timeoutMs">The timeout in millisenconds, in which the test have to complete (optional : default 97ms)</param>
    /// <returns></returns>
    public static bool IsNSSerializable ( this object @this, JsonSerializerSettings settings = null, int timeoutMs = 97)
    {
        try
        {
            TimeSpan testTimeout = TimeSpan.FromMilliseconds(timeoutMs);
            var currentSettings = settings ?? new JsonSerializerSettings()
            {
                Culture = CultureInfo.InvariantCulture,
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
                MaxDepth = 10
            };

            using (var tokenSource = new CancellationTokenSource(testTimeout))
            {
                string result = string.Empty;
                var token = tokenSource.Token;
                Thread workerThread = null;

                Task testActivity = Task.Run(() => 
                {
                    try
                    {
                        workerThread = new Thread(() =>
                        {
                            try
                            {
                                result = JsonConvert.SerializeObject(@this, settings);
                            }
                            catch (Exception err)
                            { }
                        });

                        workerThread.Join(testTimeout);
                    } 
                    catch (Exception err) 
                    { } 
                } , token);
                
                if (!testActivity.Wait(testTimeout))
                {
                    token.ThrowIfCancellationRequested();
                    global::System.Threading.Thread.Sleep(1);

                    if (workerThread != null && workerThread.IsAlive)
                        workerThread.Abort();
                }

                return !string.IsNullOrEmpty(result);
            }
        }
        catch (Exception err )
        {
            return false;
        }
    }
Related