Problems using JSON.NET with ExpandableObjectConverter
Asked Answered
K

3

11

I have the following class defined:

<TypeConverter(GetType(ExpandableObjectConverter))>
<DataContract()>
Public Class Vector3

   <DataMember()> Public Property X As Double
   <DataMember()> Public Property Y As Double
   <DataMember()> Public Property Z As Double

   Public Overrides Function ToString() As String

      Return String.Format("({0}, {1}, {2})",
                           Format(X, "0.00"),
                           Format(Y, "0.00"),
                           Format(Z, "0.00"))

   End Function

End Class

Using the DataContractJsonSerializer I receive the following JSON as expected:

{
  "Vector": {
    "X": 1.23,
    "Y": 4.56,
    "Z": 7.89
  }
}

However, JSON.NET produces:

{
  "Vector": "(1.23, 4.56, 7.89)"
}

If I remove the ExpandableObjectConverter attribute from the class, JSON.NET produces results as expected (same as DataContractJsonSerializer).

Unfortunately I need the ExpandableObjectConverter so that the class works with a property grid.

Is there any way to tell JSON.NET to ignore ExpandableObjectConverters?

I prefer to use JSON.NET instead of DataContractJsonSerializer because it is much easier to serialize enums to their string representations.

Kleist answered 9/7, 2013 at 23:37 Comment(1)
You might need to implement your own converter : #8031038Caliban
K
16

Although I appreciate Rivers' answer I am really looking for a solution that ignores all expandable object converters automatically (like the DataContractJsonSerializer does) rather than building a custom JsonConverter for each offending class.

I have found the following two solutions:

  1. Use the built in DataContractJsonSerializer instead (at the expense of some other conveniences of JSON.NET).
  2. Use a custom ExpandableObjectConverter (see below).

Since the default ExpandableObjectConverter supports converting to/from string, JSON.NET is serializing the class with a string. To counteract this I have created my own expandable object converter which does not allow conversions to/from string.

Imports System.ComponentModel

Public Class SerializableExpandableObjectConverter
   Inherits ExpandableObjectConverter

   Public Overrides Function CanConvertTo(context As System.ComponentModel.ITypeDescriptorContext, destinationType As System.Type) As Boolean

      If destinationType Is GetType(String) Then
         Return False
      Else
         Return MyBase.CanConvertTo(context, destinationType)
      End If

   End Function

   Public Overrides Function CanConvertFrom(context As System.ComponentModel.ITypeDescriptorContext, sourceType As System.Type) As Boolean

      If sourceType Is GetType(String) Then
         Return False
      Else
         Return MyBase.CanConvertFrom(context, sourceType)
      End If

   End Function

End Class

Applying the above converter works flawlessly with JSON.NET and with the property grid control!

Kleist answered 15/7, 2013 at 21:6 Comment(2)
How do you "apply the above converter" please?Bullins
@Bullins in C#, add the attribute [TypeConverter(typeof(SerializableExpandableObjectConverter))] to your classClamp
F
5

Thanks to JRS for posting this. Users of C# can use this translation from VB:

public class SerializableExpandableObjectConverter : ExpandableObjectConverter
{
    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        if(destinationType == typeof(string))
        {
            return false;
        }
        else
        {
            return base.CanConvertTo(context, destinationType);
        }
    }

    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        if(sourceType == typeof(string))
        {
            return false;
        }
        else
        {
            return base.CanConvertFrom(context, sourceType);
        }
    }
}
Fullmer answered 8/8, 2021 at 20:7 Comment(0)
L
1

Due to the fact that you are specifying a TypeConverter, Json.net uses it. To get around this, create a JsonConverter with LINQ to Json to use the format you need:

Public Class Vector3Converter
    Inherits JsonConverter

Public Overrides Sub WriteJson(writer As JsonWriter, value As Object, serializer As JsonSerializer)
    Dim vector = DirectCast(value, Vector3)

    serializer.Serialize(writer, New JObject() From { _
        {"X", vector.X}, _
        {"Y", vector.Y}, _
        {"Z", vector.Z} _
    })
End Sub

Public Overrides Function ReadJson(reader As JsonReader, objectType As Type, existingValue As Object, serializer As JsonSerializer) As Object
    Dim jObject = serializer.Deserialize(Of JObject)(reader)

    Return New Vector3() With { _
        Key .X = CDbl(jObject("X")), _
        Key .Y = CDbl(jObject("Y")), _
        Key .Z = CDbl(jObject("Z")) _
    }
End Function

Public Overrides Function CanConvert(objectType As Type) As Boolean
    Return objectType = GetType(Vector3)
End Function
End Class

Then, assign it to your type:

<TypeConverter(GetType(System.ComponentModel.ExpandableObjectConverter))> _
<DataContract> _
<JsonConverter(GetType(Vector3Converter))> _
Public Class Vector3
End Class

I originaly used C# for this and used online converter to VB so it may be a bit off.

Lemma answered 11/7, 2013 at 4:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.