anakic/Jot

Crashes on saving any object with IPAddress property

rolikoff opened this issue · 1 comments

Hi there, first of all, thanks for your effort on making this lib. We actively use Jot in our projects.

I've encountered a bug when if you have a trackable object, which has IPAddress type property, Jot crashes.

Newtonsoft.Json.JsonSerializationException
"Error getting value from 'ScopeId' on 'System.Net.IPAddress'."
------------
Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(JsonWriter writer, Object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, JsonContract& memberContract, Object& memberValue)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)
Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)
Newtonsoft.Json.JsonConvert.SerializeObjectInternal(Object value, Type type, JsonSerializer jsonSerializer)
Jot.Storage.Stores.JsonFileStore.SaveValues(Dictionary`2 values)
Jot.Storage.PersistentStoreBase.CommitChanges()
Jot.TrackingConfiguration.Persist()
Jot.StateTracker.RunAutoPersist()
Jot.StateTracker.AutoPersistTrigger_PersistRequired(Object sender, EventArgs e)
Jot.Triggers.DesktopPersistTrigger.OnApplicationClosing()
Jot.Triggers.DesktopPersistTrigger.<.ctor>b__0_0(Object s, ExitEventArgs e)
System.Windows.ExitEventHandler.Invoke(Object sender, ExitEventArgs e)
System.Windows.Application.OnExit(ExitEventArgs e)
System.Windows.Application.DoShutdown()
System.Windows.Application.ShutdownImpl()
System.Windows.Application.ShutdownCallback(Object arg)
System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
System.Windows.Threading.DispatcherOperation.InvokeImpl()
System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj)
System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext executionContext, ContextCallback callback, Object state)
System.Windows.Threading.DispatcherOperation.Invoke()
System.Windows.Threading.Dispatcher.ProcessQueue()
System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
System.Windows.Application.RunDispatcher(Object ignore)
System.Windows.Application.RunInternal(Window window)
System.Windows.Application.Run(Window window)
System.Windows.Application.Run()
MyApp.App.Main() App.xaml.cs: line: 40
System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
System.Threading.ThreadHelper.ThreadStart_Context(Object state)
System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
System.Threading.ThreadHelper.ThreadStart()

We are aware that IPAddress class is not very friendly to serialization so that is why this issue happens.
I guess what Jot needs is to derive a new class IPAddressConverter from the JsonConverter then create a new JsonSerializerSettings and add that converter to the JsonSerializedSettings instance. Then use that settings instance for JsonConvert.SerializeObject() and JsonConvert.DeserializeObject<T>()

If that sounds good to you, I can make a PR for that.

Hi! Sorry for the delay, didn't have time to look into this until now. Yeah, that sounds exactly right. It makes sense to build in support for IPAddress right into JsonFileStore since it's a built-in .NET class. It would be even better if Newtonsoft.Json did it, but it is what it is. In any case, please do make that pull request.

Maybe it would also make sense to expose a method for registering custom JsonConverter objects from outside the JsonFileStore class, so that custom converters can be registered without needing to modify Jot. (Not necessarily as part of this PR, though).