Exclude property from json serialization in ApiController - asp.net

I am trying to exclude properties from being serialized to JSON in web ApiControllers. I have verified the following 2 scenarios work.
I have included the following attributes on the property I wish to exclude.
[System.Web.Script.Serialization.ScriptIgnore]
[System.Xml.Serialization.XmlIgnore]
If I manually serialize my object using the JavaScriptSerializer, the property is excluded. Also, if I view the serialized XML output from the web ApiController, the property is excluded. The problem is, the serialized JSON via the web ApiController still contains the property. Is there another attribute that I can use that will exclude the property from JSON serialization?
UPDATE:
I realized all my tests were in a much more complex project and that I hadn't tried this in a an isolated environment. I did this and am still getting the same results. Here is an example of some code that is failing.
public class Person
{
public string FirstName { get; set; }
[System.Web.Script.Serialization.ScriptIgnore]
[System.Xml.Serialization.XmlIgnore]
public string LastName { get; set; }
}
public class PeopleController : ApiController
{
public IEnumerable<Person> Get()
{
return new[]
{
new Person{FirstName = "John", LastName = "Doe"},
new Person{FirstName = "Jane", LastName = "Doe"}
};
}
}
Here is the generated output.
JSON:
[
{
"FirstName" : "John",
"LastName" : "Doe"
},
{
"FirstName" : "Jane",
"LastName" : "Doe"
}
]
XML:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfPerson xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Person>
<FirstName>John</FirstName>
</Person>
<Person>
<FirstName>Jane</FirstName>
</Person>
</ArrayOfPerson>

Be aware that JSON serialization is changing in Web API.
In the beta release, Web API used DataContractJsonSerializer to serialize JSON. However, this has changed; the latest version of Web API uses json.net by default, although you can override this and use DataContractJsonSerializer instead.
With DataContractJsonSerializer, you can use [DataContract] attributes to control the serialization. I'm stil not very familiar with json.net, so I don't know how it controls serialization.

You can use [JsonIgnore] attribute for a JSON-specific fix; or you can use [DataContract] and [DataMember] for a fix that works both with the JSON formatter, and with the (XML) DataContractSerializer.
This article provides more detailed info on the default media-type formatters:
http://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization#json_media_type_formatter

This is a little late to the game, but IgnoreDataMember is precisely what we need in your/my scenario:
[System.Runtime.Serialization.IgnoreDataMember]
public int NotSerialized { get; set; }
According to the MSDN, IgnoreDataMember came in with .NET 3.0 SP2.

JsonIgnore modifies the entire class definition. In case you want to control specific action/request, you can try this approach.

Looks like this covers what I need. It shows you you can swap out formatters. It even includes an example formatter that uses the JavaScriptSerializer which is what I need.
http://wildermuth.com/2012/2/22/WebAPI_for_the_MVC_Guy

Related

Properties Added with Aspects Not in Generated JSON Schema

I'm trying to generate JSON Schemas using Newtonsoft JSON Schema. Regular properties added in my POCO class are added to the schema when it is generated. I'm also using PostSharp Aspects to add properties, but none of those are added to the schema.
This is a Console application targeting .NET 4.7.2.
Here is my Console application:
public class Program
{
private static void Main(string[] args)
{
JSchemaGenerator gen = new JSchemaGenerator();
JSchema schema = gen.Generate(typeof(MyClass));
File.WriteAllText("C:\\Temp\\TestSchema.json", schema.ToString());
}
}
Here is my aspect:
[PSerializable]
public class TestAspect : InstanceLevelAspect
{
[IntroduceMember]
[JsonProperty(Required = Required.Always)]
public string AspectProp { get; set; }
}
And here is my POCO:
[TestAspect]
public class MyClass
{
public int MyProperty { get; set; }
}
Finally, here is the generated schema:
{
"type": "object",
"properties": {
"MyProperty": {
"type": "integer"
}
},
"required": [
"MyProperty"
]
}
The MyProperty property is in the schema, but AspectProp - the property added by the aspect - is not.
When I open the exe in a decompiler, I can see that AspectProp is actually added to MyClass:
I'm not sure if this is a problem with PostSharp or Newtonsoft JSON Schema or if I'm doing something wrong. It seems like this should be possible.
Edit 1: 20 May
I split my solution out into separate projects - one for the Console app, one for the Aspect and one for MyClass. After making sure I was referencing the generated MyClass DLL directly (i.e. not a project reference, I actually removed the project once MyClass was built) it did not make a difference. AspectProp is still not in the schema. Based on this and the serialization suggested below by #dbc, this leads me to believe it is a problem with the Newtonsoft schema generator
Edit 2: 20 May
Per Antonin's Answer below, I created a new ContractResolver:
public class AspectPropertyResolver : DefaultContractResolver
{
public AspectPropertyResolver()
{
SerializeCompilerGeneratedMembers = true;
}
}
And then registered it in my app before calling Generate:
gen.ContractResolver = new AspectPropertyResolver();
Now my schema includes the aspect-generated property.
Newtonsoft.Json has an opt-in feature to serialize compiler-generated properties. See Newtonsoft.Json.Serialization.DefaultContractResolver.SerializeCompilerGeneratedMembers property.

Is there a way to customize the ObjectMapper used by Spring MVC without returning String?

I have a graph of objects that I'd like to return different views of. I don't want to use Jackson's #JsonViews to implement this. Right now, I use Jackson MixIn classes to configure which fields are shown. However, all my rest methods return a String rather than a type like BusinessCategory or Collection< BusinessCategory >. I can't figure out a way to dynamically configure the Jackson serializer based on what view I'd like of the data. Is there any feature built into Spring to configure which Jackson serializer to use on a per-function basis? I've found posts mentioning storing which fields you want in serialized in thread-local and having a filter send them and another post filtering based on Spring #Role, but nothing addressing choosing a serializer (or MixIn) on a per-function basis. Any ideas?
The key to me thinking a proposed solution is good is if the return type is an object, not String.
Here are the objects in my graph.
public class BusinessCategory implements Comparable<BusinessCategory> {
private String name;
private Set<BusinessCategory> parentCategories = new TreeSet<>();
private Set<BusinessCategory> childCategories = new TreeSet<>();
// getters, setters, compareTo, et cetera
}
I am sending these across the wire from a Spring MVC controller as JSON like so:
#RestController
#RequestMapping("/business")
public class BusinessMVC {
private Jackson2ObjectMapperBuilder mapperBuilder;
private ObjectMapper parentOnlyMapper;
#Autowired
public BusinessMVCfinal(Jackson2ObjectMapperBuilder mapperBuilder) {
this.mapperBuilder = mapperBuilder;
this.parentOnlyMapper = mapperBuilder.build();
parentOnlyMapper.registerModule(new BusinessCategoryParentsOnlyMapperModule());
}
#RequestMapping(value="/business_category/parents/{categoryName}")
#ResponseBody
public String getParentCategories(#PathVariable String categoryName) throws JsonProcessingException {
return parentOnlyMapper.writeValueAsString(
BusinessCategory.businessCategoryForName(categoryName));
}
}
I have configure the serialization in a MixIn which is in turn added to the ObjectMapper using a module.
public interface BusinessCategoryParentsOnlyMixIn {
#JsonProperty("name") String getName();
#JsonProperty("parentCategories") Set<BusinessCategory> getParentCategories();
#JsonIgnore Set<BusinessCategory> getChildCategories();
}
public class BusinessCategoryParentsOnlyMapperModule extends SimpleModule {
public BusinessCategoryParentsOnlyMapperModule() {
super("BusinessCategoryParentsOnlyMapperModule",
new Version(1, 0, 0, "SNAPSHOT", "", ""));
}
public void setupModule(SetupContext context) {
context.setMixInAnnotations(
BusinessCategory.class,
BusinessCategoryParentsOnlyMixIn.class);
}
}
My current solution works, it just doesn't feel very clean.
"categories" : [ {
"name" : "Personal Driver",
"parentCategories" : [ {
"name" : "Transportation",
"parentCategories" : [ ]
} ]
}
Oh yes, I'm using:
spring-boot 1.2.7
spring-framework: 4.1.8
jackson 2.6.3
Others listed here: http://docs.spring.io/spring-boot/docs/1.2.7.RELEASE/reference/html/appendix-dependency-versions.html
In the end, the only process that met my needs was to create a set of view objects which exposed only the fields I wanted to expose. In the grand scheme of things, it only added a small amount of seemingly unnecessary code to the project and made the flow of data easier to understand.

ViewModel type architecture from Controller to View

I have a fairly complex class of Policies, of which I display a checkbox list of them, the user checks which one they want, and returns back to the server via ajax. The class is fairly complex:
public class Policy {
public int PolicyId { get; set; }
public string PolicyName { get; set; }
... another 15 properties ...
}
To display the list of checkboxes I really only need the Id and Name, so I've created a lightweight class PolicyViewModel that is simply:
public class PolicyViewModel {
public int PolicyId { get; set; }
public string PolicyName { get; set; }
}
So I then pass a List to the View and get a List back containing the selected Policies.
Another developer on my team said that he doesn't necessarily want to translate from the ViewModel to the Policy class on the Ajax call to save the selected policies, but I'm resistant to send a List of policies due to how heavy they are to send to the view, retrieving all the properties, etc.
EDIT: For clarification, on the Ajax save method, to persist to the DB, the call needs a list of the full Policy class.
What is the best way to display this list and get back the values? Is there a better way than I am proposing?
Usually, you wouldn't need a separate model when serializing to json. Simply pluck out what you need from the domain object into an anonymous object.
return policies.Select(x => new { PolicyId = x.PolicyId, Name = x.PolicyName});
on the return trip, you shouldn't have to send anything more than the Ids of the policies that the user selected. Those can be easily mapped back to your policy objects.
public Whatever PostPolicyChoices(IEnumerable<int> ids)
{
var checked = _context.Policies.Where(x => returnIds.Contains(x.PolicyId));
// snip
boom. done.
I will recommend you not to work with Domain objects in your mvc application . You must work just with ViewModels, I think this is best practice for mvc projects. Take a look at Automapper and use it in your project, this will simplify your work, so this should look something like this :
in your [HttpGet] method you will have :
var model =Mapper.Map<IList<Policy>,IList<VmSysPolicy>>(yourlist)
And in your [HttpPost] method you will have :
var domainList=Mapper.Map<IList<VmSysPolicy>,IList<Policy>>(modelList);
And in your mapping configuration you will do :
Mapper.CreateMap<Policy,PolicyVmSysPolicy,>()
.ForMemeber()//Your mapping here
and
Mapper.CreateMap<VmSysPolicy,Policy>()
.ForMemeber//mapping here

How to add validation to my POCO(template) classes

So I used this tutorial to generate my poco classes which I am to use throughout my aplication.. the problem is that Im not supposed to modify the generated cs files cause they get autoregenerated... How do I add attributes like [Required] and stuff like that?? please help
You can't add it directly (unless you modify T4 template to create them for you) but you can try to use trick introduced in ASP.NET dynamic data. All POCO classes are defined as partial. So lets define your partial part:
using System.ComponentModel.DataAnnotations;
[MetadataType(typeof(MyClassMetadata))]
public partial class MyClass
{
private class MyClassMetadata
{
[Required]
public object Id;
[Required]
[StringLength(100)]
public object Name;
}
}
Metadata class is special type to hold only metadata - it is never used. Name of fields must be same as corresponding fields in real class (field types doesn't matter so you can use object).
Anyway in ASP.NET MVC you should use specialized View model for each view and pass data you need so the validation attributes will be placed in view model class.
The attributes on the generated POCOs are derived from the facets on the entities in the model. e.g. for [Required] make sure the field is "not null" and for [StringLength(n)] make sure the datatype is nvarchar(n) via the MaxLength facet.
Further expanding on the answer. By using Microsoft Patterns & Practices Enterprise Library 5 Validation Block, you can open up a wealth of validation possibilities beyond those available through normal data annotations.
using Microsoft.Practices.EnterpriseLibrary.Validation;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
[HasSelfValidation]
public partial class Category : ICategory
{
[SelfValidation]
public void Validate(ValidationResults validationResults)
{
if (this.Title === "Credo")
{
validationResults.AddResult(
new ValidationResult(
"Category title cannot be a veiled reference to a former cool 2000AD character.",
this,
null,
null,
null));
}
validationResults.AddAllResults(
ValidationFactory
.CreateValidator<ICategory>()
.Validate(this));
}
}
using System;
using System.ComponentModel.DataAnnotations;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
public interface ICategory
{
int Id
{
get;
set;
}
[Required]
[StringLengthValidator(1, 50, MessageTemplate = "Category title should be a maximum of 50 characters in length.")]
string Title
{
get;
set;
}
}

How to return an interface or a "complex" value using a webservice?

I have a webservice that returns a complex value (this is a tree)
public class DocumentTreeNode
{
public MyDocument Data
{ get; set;}
private LinkedList<DocumentTreeNode> _children;
public IEnumerable<DocumentTreeNode> Children
{
get
{
return _children.AsEnumerable();
}
}
(...)
}
Unfortunately, when I call the webservice, it returns
<?xml version="1.0" encoding="utf-8"?>
<DocumentTreeNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://tempuri.org/" />
I have double checked, and the object I am supposed to return is not empty.
I'm guessing that means ASP.Net is unable to properly serialize my class (due to the IEnumerable?). I was going to use an Xml Serialization and have my service return an XmlDocument, but I don't think this is the best way.
What is the easiest way to pass this object through (with minimal work) ?
Properties that return interfaces are skipped by the serializer. It requires a concrete type, otherwise re-hydration would be impossible without additional type metadata sent along, which is not part of the OOTB serialization.

Resources