Reputation: 3161
I have a view model. That view model has an implementation. The implementation is a c# class. The c# class has properties and attributes. I would like to serialize the properties and attributes in to a JSON object. I don't need any of the data it's the validation details that I require in the JSON object. The following is my view model:
public class CreateProjectViewModel
{
public SelectList Categories;
public SelectList Locations;
public CreateProjectViewModel() { }
public CreateProjectViewModel(SelectList categories, SelectList locations)
{
this.Categories = categories;
this.Locations = locations;
}
[Required(ErrorMessage = "You must provide a unique name for your project.")]
[Display(Name = "Project name")]
[MaxLength(128)]
public string Name { get; set; }
[Required(ErrorMessage = "Give a succinct description of the issues your project is designed to address")]
[Display(Prompt = "E.g Reduce plastic waste and save our oceans!")]
public string Description { get; set; }
}
Upvotes: 0
Views: 1940
Reputation: 27039
Use NewtonSoft:
var vm = new CreateProjectViewModel();
string serialized = JsonConvert.SerializeObject(vm);
EDIT
Could you extend your answer to include the serialisation of the view model annotations such as [Display(Name = "Project name")] and [MaxLength(128)]?
In other words "Can I serialize the attributes applied to properties?"
This is not something which is available out of the box in most libraries. We will need to create our own custom converter for this. Luckily, NewtonSoft gives us the ability to write our own converters. This one is not trivial but doable. Here is a custom converter which will do serialize the attributes.
Custom Converter
public class KeysJsonConverter<T> : JsonConverter {
private readonly Type[] _types;
public KeysJsonConverter(params Type[] types) {
_types = types;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
JToken t = JToken.FromObject( value );
if( t.Type != JTokenType.Object ) {
t.WriteTo( writer );
}
else {
JObject o = ( JObject ) t;
IList<JProperty> jProperties = o.Properties().ToList();
PropertyInfo[] viewModelProperties = typeof( T ).GetProperties();
foreach( var thisVmProperty in viewModelProperties ) {
object[] thisVmPropertyAttributes = thisVmProperty.GetCustomAttributes( true );
var jObjectForVmProperty = new JObject();
var thisProperty = jProperties.Single( x => x.Name == thisVmProperty.Name );
jObjectForVmProperty.Add( "ActualValue", thisProperty.Value );
foreach( var thisVmPropertyAttribute in thisVmPropertyAttributes ) {
if (thisVmPropertyAttribute is MaxLengthAttribute ) {
CreateObjectForProperty( thisVmPropertyAttribute as MaxLengthAttribute,
jObjectForVmProperty );
}
else if (thisVmPropertyAttribute is RequiredAttribute ) {
CreateObjectForProperty( thisVmPropertyAttribute as RequiredAttribute,
jObjectForVmProperty );
}
else if (thisVmPropertyAttribute is DisplayAttribute ) {
CreateObjectForProperty( thisVmPropertyAttribute as DisplayAttribute,
jObjectForVmProperty );
}
else {
continue; // Put more else if conditions like above if you want other types
}
}
thisProperty.Value = jObjectForVmProperty;
}
o.WriteTo( writer );
}
}
private void CreateObjectForProperty<TAttribute>(TAttribute attribute, JObject jObjectForVmProperty)
where TAttribute : Attribute
{
var jObjectForAttriubte = new JObject();
var max = attribute as TAttribute;
var maxPropeties = typeof( TAttribute ).GetProperties();
foreach( var thisProp in maxPropeties ) {
try {
jObjectForAttriubte.Add( new JProperty( thisProp.Name, thisProp.GetValue( max ) ) );
}
catch( Exception ex) {
// No need to worry. Fails on complex attribute properties and some other property types. You do not need this.
// If you do, then you need to take care of it.
}
}
jObjectForVmProperty.Add( typeof( TAttribute ).Name, jObjectForAttriubte );
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
throw new NotImplementedException( "Unnecessary because CanRead is false. The type will skip the converter." );
}
public override bool CanRead
{
get { return false; }
}
public override bool CanConvert(Type objectType) {
return _types.Any( t => t == objectType );
}
}
The converter above is generic so it will work for any type. Right now it will serialize these attributes: MaxLengthAttribute
, RequiredAttribute
, DisplayAttribute
. But to do other ones is super easy: Just create another condition as I have commented in the continue
section.
The value for the property is stored in a property named ActualValue
.
Usage
var vm = new CreateProjectViewModel() { Description = "My long description",
Name = "StackOverflow" };
string json = JsonConvert.SerializeObject( vm, Formatting.Indented,
new KeysJsonConverter<CreateProjectViewModel>
( typeof( CreateProjectViewModel ) ) );
Output
Here is the output when I used your CreateProjectViewModel
:
{
"Categories": null,
"Locations": null,
"Name": {
"ActualValue": "StackOverflow",
"RequiredAttribute": {
"AllowEmptyStrings": false,
"RequiresValidationContext": false,
"ErrorMessage": "You must provide a unique name for your project.",
"ErrorMessageResourceName": null,
"ErrorMessageResourceType": null
},
"DisplayAttribute": {
"ShortName": null,
"Name": "Project name",
"Description": null,
"Prompt": null,
"GroupName": null,
"ResourceType": null
},
"MaxLengthAttribute": {
"Length": 128,
"RequiresValidationContext": false,
"ErrorMessage": null,
"ErrorMessageResourceName": null,
"ErrorMessageResourceType": null
}
},
"Description": {
"ActualValue": "My long description",
"RequiredAttribute": {
"ErrorMessage": "Give a succinct description of the issues your project is designed to address",
"AllowEmptyStrings": false,
"RequiresValidationContext": false,
"ErrorMessageResourceName": null,
"ErrorMessageResourceType": null
},
"DisplayAttribute": {
"Name": null,
"ShortName": null,
"Description": null,
"Prompt": "E.g Reduce plastic waste and save our oceans!",
"GroupName": null,
"ResourceType": null
}
}
}
Upvotes: 4