1

Moving away from BinaryFormaters, I started to use Text.Json.

I'm trying to serialize/deserialize a simple class containing a dynamic object, named 'PropertyValue'. ' PropertyType' is set by the application. In this sample, PropertyValue is a System.Drawing.Point

Public Class FormSetting
    Public Property FormName As String
    Public Property ControlName As String
    Public Property PropertyName As String
    Public Property PropertyType As String
    Public Property PropertyValue As Object
End Class

Serializer:

Using sw As New IO.StreamWriter(SettingLocation, IO.FileMode.Create)
    Dim jsonString = JsonSerializer.Serialize(SettingList, JsonOptions)
    sw.Write(jsonString)
End Using

Deserialize

Using fs As New IO.FileStream(SettingLocation, IO.FileMode.Open)
   Dim FormSettingResult As FormSetting()
   FormSettingResult = JsonSerializer.Deserialize(Of FormSetting())(fs,LoadSettings.AddRange(FormSettingResult)
End Using

The serialized json looks like:

  {
    "FormName": "UnlockForm",
    "ControlName": "UnlockForm",
    "PropertyName": "Location",
    "PropertyType": "Point",
    "PropertyValue": {
      "X": 304,
      "Y": 243
    }
  }

note that the serialized data doesn't save the original type (point in this case), the deserialized PropertyValue value is:

RootElement = ValueKind = Object : "{
      "X": 304,
      "Y": 243
    }"

and not the expected type Drawing.Point, with valued Point(304,243).

Is there a JSON method to convert ValueKind to a the desired object? for example using something like (a pseudo) json.ConvertObject:

  dim PointLocation = json.ConvertObject(of Drawing.Point)(item.PropertyValue)
dbc
  • 104,963
  • 20
  • 228
  • 340
fcm
  • 1,247
  • 15
  • 28
  • You'll have to write your own JsonConverter. For example, see: [Deserialize inferred types to object properties](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-6-0#deserialize-inferred-types-to-object-properties) and [Support polymorphic deserialization](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-6-0#support-polymorphic-deserialization) – Jimi Jan 22 '22 at 19:07
  • @Jimi Write converter is a good idea, however: **a)** Write converters on Visual Basic is not supported and **b)** There are a few hundred types just on System.Drawing, a very tedious job. – fcm Jan 22 '22 at 21:37
  • You're serializing an `Object` type that cannot be inferred or converted by a non-specialized TypeConveter (a PointConveter is required, or a custom method), so there you are. You could use, e.g., `dim point = Activator.CreateInstance(Type.GetType(item.PropertyType))` (where `PropertyType` is the fully qualified name of the Type, not `Point` but `System.Drawing.Point, System.Drawing.Primitives, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a` - .Net 5), then use Reflection to enumerate the Properties of the type you created [...] – Jimi Jan 22 '22 at 22:42
  • [...] and do the same with the JsonElement you get back from deserialization (with, e.g., `EnumerateObject()` or `GetProperty()` or `TryGetProperty()`), then assign the values to properties with the same name. -- Just an idea, it doesn't require a converter. -- Or, write the converter in C# using a similar logic. Or don't use Object as data type. Or move back to Json.Net, I doubt this kind of *type inference* will ever be added to System.Text.Json, the people involved are quite firm on this. – Jimi Jan 22 '22 at 22:43
  • @Jimi this works 'System.Text.Json.JsonSerializer.Deserialize(JsonValue.ToString, type.gettype( p.PropertyType))' but need a few exceptions manual for simple types (string and boolean thus far), also fails doing drawing.color. My issue is that on my app, it's hard to know what types need to be serialized: need to be ready for the full NET6 rooster!!! – fcm Jan 22 '22 at 22:55
  • I doubt you get back a Point from that `.Deserialize(JsonValue.ToString, type.gettype( p.PropertyType))`. `Type.GetType()` needs the fully qualified Type and, IIRC, the serialized Object contains values that are not related to a Point Type. That's why I suggested to specify the full name of the Type and use Activator + Reflection to generate a Type, enumerate and set the properties. So you don't need to know what Type you have serialized or what properties it has. – Jimi Jan 22 '22 at 23:05
  • @Jimi actually works fine with Point, Size and most classes... try yourself. Actually I'm using 'System.Drawing.Point'... partially qualified. – fcm Jan 23 '22 at 00:14
  • Take a look at [Is polymorphic deserialization possible in System.Text.Json?](https://stackoverflow.com/q/58074304/3744182). You will need to create a custom `JsonConverter` for `PropertyValue`. Your option are: 1) If you can infer the type from the fields (e.g. the presence of `X` and `Y` means it's a Point) then you can preload into a `JsonDocument` and make the inference. 2) If you cannot infer the type from the fields, you will need to inject some sort of type discriminator property. Does that question answer yours also? Can we mark this as a duplicate? – dbc Jan 29 '22 at 21:26
  • But don't just inject the .NET type name and deserialize unconditionally to that type! If you do you will have the same security vulnerability as BinaryFormatter as well as Json.NET with TypeNameHandling enabled. For details see [TypeNameHandling caution in Newtonsoft Json](https://stackoverflow.com/q/39565954/3744182). – dbc Jan 29 '22 at 21:27
  • Temporary, I'm using a custom deserializer with a (huge) case based on the saved type. Is working but is not very elegant. – fcm Jan 31 '22 at 00:53

0 Answers0