9

很久的奮鬥以這個...JSON.Net JsonConverter爲DbGeography

基本上我有一個DbGeography屬性模型的第一EF5對象。我想申請一個JsonConverter,讓它將其作爲簡單的緯度/經度值進行往返。我正在使用WebAPI。

尋找JSON輸出和輸入,像這樣:

{ 
    "location": 
    { 
     "geopoint": 
     { 
      "latitude":40.770712, 
      "longitude":-73.962011 
     } 
    } 
} 

這裏是我的類定義和JsonConverter:

[MetadataType(typeof(QueryLocationMetadata))] 
partial class Location 
{ 
} 

public class QueryLocationMetadata 
{ 
    [JsonConverter(typeof(DbGeographyConverter))] 
    public virtual DbGeography GeoPoint { get; set; } 
} 


public class DbGeographyConverter : JsonConverter 
{ 
    private const string LATITUDE_KEY = "latitude"; 
    private const string LONGITUDE_KEY = "longitude"; 

    public override bool CanConvert(Type objectType) 
    { 
     return objectType.Equals(typeof(DbGeography)); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     if (reader.TokenType == JsonToken.Null) 
      return default(DbGeography); 

     var jObject = JObject.Load(reader); 

     if (!jObject.HasValues || (jObject.Property(LATITUDE_KEY) == null || jObject.Property(LONGITUDE_KEY) == null)) 
      return default(DbGeography); 

     string wkt = string.Format("POINT({1} {0})", jObject[LATITUDE_KEY], jObject[LONGITUDE_KEY]); 
     return DbGeography.FromText(wkt, DbGeography.DefaultCoordinateSystemId); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     var dbGeography = value as DbGeography; 

     serializer.Serialize(writer, dbGeography == null || dbGeography.IsEmpty ? null : new { latitude = dbGeography.Latitude.Value, longitude = dbGeography.Longitude.Value }); 
    } 
} 

所以用這個我能成功序列化和反序列化,即使對象正確,但在我打我的ApiController行動之前,我得到以下錯誤:

System.Data.SqlTypes.SqlNullValueException: Data is Null. This method or property cannot be called on Null values. 
    at System.Data.SqlTypes.SqlDouble.get_Value() 
    at GetValueFromSqlDouble(Object) 
    at System.Web.Http.Metadata.Providers.AssociatedMetadataProvider`1.<>c__DisplayClass3.<GetMetadataForPropertiesImpl>b__0() 
    at System.Web.Http.Metadata.ModelMetadata.get_Model() 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateProperties(ModelMetadata metadata, ValidationContext validationContext) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateProperties(ModelMetadata metadata, ValidationContext validationContext) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateProperties(ModelMetadata metadata, ValidationContext validationContext) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateProperties(ModelMetadata metadata, ValidationContext validationContext) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateElements(IEnumerable model, ValidationContext validationContext) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateProperties(ModelMetadata metadata, ValidationContext validationContext) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateElements(IEnumerable model, ValidationContext validationContext) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.Validate(Object model, Type type, ModelMetadataProvider metadataProvider, HttpActionContext actionContext, String keyPrefix) 
    at System.Web.Http.ModelBinding.FormatterParameterBinding.<>c__DisplayClass1.<ExecuteBindingAsync>b__0(Object model) 
    at System.Threading.Tasks.TaskHelpersExtensions.<>c__DisplayClass36`1.<>c__DisplayClass38.<Then>b__35() 
    at System.Threading.Tasks.TaskHelpersExtensions.<>c__DisplayClass49.<ToAsyncVoidTask>b__48() 
    at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1 func, CancellationToken cancellationToken) 

在玩弄各種各樣的事情之後,我絕對不知所措。我通常會嘗試驗證該屬性,但使用SqlDouble?

回答

4

問題不在於您的轉換器。這是公共屬性的getter在對象圖中引發時發生的已知驗證錯誤。這項工作項跟蹤問題:

http://aspnetwebstack.codeplex.com/workitem/740

在此同時,你應該能夠通過禁用驗證繞過它:

config.Services.Clear(typeof(ModelValidatorProvider)); 

很抱歉給您帶來不便。

+0

臨時的,至少,讓我保留一些頭髮在我的頭上。謝謝! – Arno

+0

您可能需要使用完全限定的名稱:System.Web.Http.Validation.ModelValidatorProvider,如下所述:https://aspnetwebstack.codeplex.com/workitem/55 – Scott

+0

@Youssef,請查看我的鏈接回答。我很想知道這是否是已知問題。 – joelmdev

4

Youssef提到的錯誤已經被修復,但是在爲DbGeography創建自定義JsonConverter時,我仍然遇到了問題,因爲驗證過程中DefaultBodyModelValidator卡住了一個循環。我的解決方案不是完全禁用模型驗證,而是使用從深度驗證中排除DbGeography類型的派生類型替換默認驗證程序。

以免重複自己,you can see the full solution here.

+0

你爲我節省了一天的憤怒,這個DbGeogrphy是......真的是我一生中看到的毫無價值的類型。 –

相關問題