JSON deserialization not working for polymorphic attribute behavior-
Sample code -
--MODEL
public class A
{
public string a_property { get; set; }
}
public class B : A
{
public string b_property { get; set; }
}
public class C
{
public A a { get; set; }
}
--API
public partial class TestController : ApiBaseController
{
[HttpGet]
public IHttpActionResult GetC()
{
return Ok<C>(new C(){a = new B(){a_property="test", b_property ="test1"}});
}
[HttpPost]
public IHttpActionResult SaveC(C c)
{
return Ok<C>(c);
}
}
--Web Route config
Config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Objects;
Config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
Config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.All;
From UI GetC API is called the object returned is C with attribute a of type B. It does have the type detail i.e. [$type A, assembly name]. When the same JSON is posted from client to call SaveC API the deserialized JSON is of type C but the attribute a is of type A instead of B.
I have searched the web but with no help, what am i missing?
JSON.net deserializer is just respecting your model, if you want specify the heritage level for your entity, you need to create a custom converter or you can work with an interface and just use a jsonconvert attribute.
Related
So it happens that you can prevent breeze json serialization of some properties using data annotations on your model by like this(well if you are using EF6 with JSON.NET on the backend)...
[Table("Project")]
public partial class Project
{
public Project()
{
}
public int id { get; set; }
[JsonIgnore]
public bool NoLongerExist { get; set; }
}
By doing so the property becomes invisible on this endpoint used by breeze
public IQueryable<Project> Projects()
{
return _db.Context.Projects.Where(o => o.NoLongerExist == true);
}
Can i apply [JsonIgnore] based on a certain condition like an authenticated user or a random if from this endpoint?
Returning the following object excludes the property "coordinates" from the JSON.
What am I doing wrong?
[Route("/GeozonePolygon/{ZoneType}")]
public class RequestGeozonePolygon{
public int ZoneType { get; set; }
}
public class ResponseGeozonePolygon{
public FeatureCollection Result { get; set; }
public ResponseStatus ResponseStatus { get; set; }
}
public class GeozonePolygon : Service {
public ResponseGeozonePolygon Any(RequestGeozonePolygon request){
return new ResponseGeozonePolygon() { Result = (new DAL.GeoZone()).GetZoneGeoJsonByType(request.ZoneType) };
}
}
These are the involved types:
public class Geometry {
public string type {
get { return GetType().Name; }
}
}
public class Feature {
public string type {
get { return GetType().Name; }
}
public Geometry geometry { get; set; }
public object properties { get; set; }
}
public class FeatureCollection {
public string type {
get { return GetType().Name; }
}
public Feature[] features { get; set; }
}
public class MultiPolygon : Geometry {
public double[][][][] coordinates { get; set; }
}
FeatureCollection property geometry contains a MultiPolygon object.
Thanks in advance!
You are serializing only the properties of the Geometry object, even though the actual object is a MultiPolygon. As explained by Mythz,
As there is no concept of 'type info' in the JSON spec, in order for inheritance to work in JSON Serializers they need to emit proprietary extensions to the JSON wireformat to include this type info - which now couples your JSON payload to a specific JSON serializer implementation.
To enable support for polymorphic Geometry objects in Servicestack.text, add a type specific config setting to add 'type info' to the output. i.e.:
JsConfig<Geometry>.ExcludeTypeInfo = false;
(may also require adding an interface. See the tests for Polymorphic List serialization. and tests for Polymorphic Instance serialization. for examples)
If you are loath to expose type info in your json, you can use custom serializers as an alternative solution.
My controller returns a JsonResult like that:
return Json(model);
How can I modify the json data on the fly before it is sent back to the client. I'd like to add validation attributes to my model so I end up with something like:
{"Label": "Test",
"ValidationRules":[{"data-val-required":"This field is required.", "data-val-length-max":25, "data-val-length":"Max 25 chars." }]}
UPDATE
public class Product
{
[Required]
String Label {get; set;}
}
when calling Json(model) with model being an instance of Product, I'd like to modify the json string before it is returned, so that it includes validation attributes.
Why not create a base class called ValidatableBase that has a ValidationRules property:
public class Product : ValidatableBase
{
public string Label { get; set; }
}
public abstract class ValidatableBase
{
public ValidatableBase()
{
this.ValidationRules = new Dictionary<string, string>();
}
public Dictionary<string, string> ValidationRules { get; set; }
}
public ActionResult GetProduct()
{
var product = new Product();
product.Label = "foo";
product.ValidationRules.Add("data-val-required", "this field is required");
return Json(product);
}
Inherit from this class and serialize.
Or if you're using DataAnnotations why not use the default jQuery validation and HtmlHelper methods provided by ASP.NET MVC?
I'm quite new about MVC. I have the following Model classes:
public class Store
{
public PriceList PriceListInfo { get; set; }
public IStore storeData;
}
public class PriceList
{
public int id { get; set; }
public string codice { get; set; }
}
public interface IStore
{
[...]
}
public class Silo2Store : IStore
{
public int S2 { get; set; }
public int S3 { get; set; }
}
And i want use this model in my view:
#model Store
#Html.TextBoxFor(model => ((Silo2Store)Model.storeData).S3)
The corresponding Controller method is:
public ActionResult Customer()
{
using (Store t = (Store)Session["Store"])
{
if (t.PriceListInfo == null)
{
t.PriceListInfo = new PriceList();
}
t.PriceListInfo.codice = "XXX";
return View(t);
}
}
And I'd like to retrieve the model in my Controller:
[HttpPost]
public ActionResult Customer(Store modelStore)
{
var test = ((Silo2Store)Model.storeData).S3;
}
but Model.storeData attribute isn't initialized in my view, it's null. Then, I can't retrieve the value in my controller.
Should I change my model in anyway?
You have to define your own model binder for IStore.
Taken from this article on MSDN Magazine about MVC Model Binding:
For example, even though the Microsoft .NET Framework provides excellent support for object-oriented principles, the DefaultModelBinder offers no support for binding to abstract base classes and interfaces.
I have an object which I am de-serializing using ToJson<>() method from ServiceStack.Text namespace.
How to omit all the GET only propeties during serialization? Is there any attribute like [Ignore] or something that I can decorate my properties with, so that they can be omitted?
Thanks
ServiceStack's Text serializers follows .NET's DataContract serializer behavior, which means you can ignore data members by using the opt-out [IgnoreDataMember] attribute
public class Poco
{
public int Id { get; set; }
public string Name { get; set; }
[IgnoreDataMember]
public string IsIgnored { get; set; }
}
An opt-in alternative is to decorate every property you want serialized with [DataMember]. The remaining properties aren't serialized, e.g:
[DataContract]
public class Poco
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
public string IsIgnored { get; set; }
}
Finally there's also a non-intrusive option that doesn't require attributes, e.g:
JsConfig<Poco>.ExcludePropertyNames = new [] { "IsIgnored" };
Dynamically specifying properties that should be serialized
ServiceStack's Serializers also supports dynamically controlling serialization by providing conventionally named ShouldSerialize({PropertyName}) methods to indicate whether a property should be serialized or not, e.g:
public class Poco
{
public int Id { get; set; }
public string Name { get; set; }
public string IsIgnored { get; set; }
public bool? ShouldSerialize(string fieldName)
{
return fieldName == "IsIgnored";
}
}
More examples in ConditionalSerializationTests.cs
For nullable members, you also have the ability to set it to null before serializing.
This is particularly useful if you want to create a single view/api model that is re-used for several API calls. The service can touch it up before setting it on the response object.
Example:
public SignInPostResponse Post(SignInPost request)
{
UserAuthentication auth = _userService.SignIn(request.Domain, true, request.Username, request.Password);
// Map domain model ojbect to API model object. These classes are used with several API calls.
var webAuth = Map<WebUserAuthentication>(auth);
// Exmaple: Clear a property that I don't want to return for this API call... for whatever reason.
webAuth.AuthenticationType = null;
var response = new SignInPostResponse { Results = webAuth };
return response;
}
I do wish there was a way to dynamically control the serialization of all members (including non-nullable) on a per endpoint fashion.