Проблемы с использованием 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 для каждого нарушающего класса.
Я нашел следующие два решения:
- Вместо этого используйте встроенный DataContractJsonSerializer (за счет некоторых других удобств JSON.NET).
- Используйте пользовательский 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, так что это может быть немного не так.