Проблемы с использованием JSON.NET с ExpandableObjectConverter

У меня определен следующий класс:

<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

С использованием DataContractJsonSerializer Я получаю следующий JSON, как и ожидалось:

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

Тем не менее, JSON.NET производит:

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

Если я удалю ExpandableObjectConverter Атрибут класса JSON.NET выдает ожидаемые результаты (так же, как DataContractJsonSerializer).

К сожалению мне нужно ExpandableObjectConverter так что класс работает с сеткой свойств.

Есть ли способ сказать JSON.NET игнорировать ExpandableObjectConverters?

Я предпочитаю использовать JSON.NET вместо DataContractJsonSerializer потому что намного проще сериализовать перечисления в их строковые представления.

3 ответа

Решение

Хотя я ценю ответ Риверса, я действительно ищу решение, которое автоматически игнорирует все конвертируемые расширяемые объекты (как это делает DataContractJsonSerializer), а не создает собственный JsonConverter для каждого нарушающего класса.

Я нашел следующие два решения:

  1. Вместо этого используйте встроенный DataContractJsonSerializer (за счет некоторых других удобств JSON.NET).
  2. Используйте пользовательский ExpandableObjectConverter (см. Ниже).

Поскольку ExpandableObjectConverter по умолчанию поддерживает преобразование в / из строки, JSON.NET сериализует класс со строкой. Чтобы противодействовать этому, я создал собственный конвертер расширяемых объектов, который не допускает преобразования в / из строки.

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

Применение вышеуказанного конвертера безупречно работает с JSON.NET и с сеткой управления свойствами!

Спасибо JRS за публикацию. Пользователи C# могут использовать этот перевод из 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);
        }
    }
}

В связи с тем, что вы указываете TypeConverter, Json.net использует его. Чтобы обойти это, создайте JsonConverter с LINQ to Json, чтобы использовать нужный вам формат:

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

Затем присвойте его вашему типу:

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

Первоначально я использовал C# для этого и использовал онлайн-конвертер в VB, так что это может быть немного не так.

Другие вопросы по тегам