使用JSON.NET与ExpandableObjectConverter的问题

时间:2013-07-09 23:37:17

标签: c# json vb.net json.net typeconverter

我定义了以下类:

<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 个答案:

答案 0 :(得分:6)

虽然我很欣赏Rivers的回答,但我真的在寻找一种自动忽略所有可扩展对象转换器的解决方案(比如DataContractJsonSerializer),而不是为每个违规类构建一个自定义JsonConverter。

我找到了以下两种解决方案:

  1. 使用内置的DataContractJsonSerializer(以牺牲JSON.NET的其他一些便利为代价)。
  2. 使用自定义ExpandableObjectConverter(见下文)。
  3. 由于默认的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和属性网格控件!

答案 1 :(得分:1)

由于您正在指定TypeConverter,Json.net使用它。要解决这个问题,请使用LINQ to Json创建一个JsonConverter以使用您需要的格式:

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,所以它可能有点偏。

答案 2 :(得分:0)

感谢 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);
        }
    }
}