user6201138 user6201138 - 2 months ago 27
C# Question

Web Api Ignoring Custom JsonConverter for DbGeography

Major struggles with this. So I'm trying to send nested Json data to my web api. I have a custom son converter for my DbGeography type. However, when i send my request, it seems like my custom converter is never called. I get error saying: Unable to cast object type Object to DbGeography. I am using a database first EF for my data model.
Commenting out the locationAccountCreated property, everything else works fine and the data is persisted successfully to the database.

Please any help would be very much appreciated.

Please relevant see code below

Controller

[Route("Create")]
[HttpPost]
public HttpResponseMessage PostNewAccount([FromBody]IDictionary<string, object> formData)
{
if (formData != null)
{
var nValueCol = formData;

var account = new Account()
{
Email = (string)nValueCol["email"],
Password = (string)nValueCol["password"],
AgreedToTerms = Convert.ToBoolean(nValueCol["agreesToTerms"]),
LocationAccountCreated = (DbGeography)nValueCol["userLocation"]//source of error
//LocationAccountCreated = (DbGeography)(IDictionary<string, IDictionary<string, double>>)nValueCol["userLocation"]
};

//...rest of code unrelated to problem
}


Custom Conveter

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) // checks if token is null.
{
return default(DbGeography);
}

var jObject = JObject.Load(reader);

if (!jObject.HasValues || (jObject.Property(LATITUDE_KEY)) == null || (jObject.Property(LONGITUDE_KEY)) == null) //checks if Json Object is null, and if longitude or latitude properties are null
{
return default(DbGeography);
}

string wellKnownText = string.Format("POINT({0} {1})", jObject[LATITUDE_KEY], jObject[LONGITUDE_KEY]);
return DbGeography.FromText(wellKnownText, 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 });
}
}


public class QueryLocationMetadata
{
[JsonConverter(typeof(DbGeographyConverter))]
public virtual System.Data.Entity.Spatial.DbGeography LocationAccountCreated { get; set; }
}


Partial Class

[MetadataType(typeof(QueryLocationMetadata))]
public partial class Account
{
}


Global.asax

protected void Application_Start()
{
var config = GlobalConfiguration.Configuration;

//Registering routes from the WebApi.Config file
GlobalConfiguration.Configure(Config.WebApiConfig.Register);


//Registering Autofac
GlobalConfiguration.Configure(Config.AutofacConfig.Initialize);

//Registering Formatter
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new DbGeographyConverter());
}


Fiddler Request

http://localhost:9576/Account/Create

User-Agent: Fiddler
Content-type: application/json
Host: localhost:9576
Content-Length: 316


{
"email":"e02f6rt7fgvujtgv@myuct.ac.za",
"firstName":"myFName",
"lastName":"myFName",
"dateOfBirth":"1992-02-18",
"password":"password",
"agreesToTerms":true,
"userName" : "Cool User",
"gender" : "Female",
"userLocation": {"geopoint":{"latitude" : 40.334910, "longitude" : -32.254586}}
}

Answer

Try inserting it as the first provider:

config.Formatters.JsonFormatter.SerializerSettings.Converters.Insert(0, new DbGeographyConverter());

I tried this in a Web Api 2 Controller with your converter code like this:

public void Post([FromBody]System.Data.Entity.Spatial.DbGeography value)

Take care of number formatting issues also. The WKT part might need to look something like this:

string wellKnownText = string.Format("POINT({0} {1})", jObject[LATITUDE_KEY].Value<double>().ToString(System.Globalization.NumberFormatInfo.InvariantInfo), jObject[LONGITUDE_KEY].Value<double>().ToString(System.Globalization.NumberFormatInfo.InvariantInfo));
Comments