Simplify object on JSON serialisation - c#

Given the following 2 classes (edited for brevity), I am generating a list of questions. Each question is assigned a 'UserAccount'.
I then want to serialise the list of questions, but for each UserAccount, I only want to write the 'Id' property (exclude any other properties on the UserAccount object).
I think I need a custom converter to perform this, but am unsure how to go about intercepting any occurrence of the UserAccount property, and only serialise the Id property on it.
public class Question
{
public int Id { get; set; }
public string Ask { get; set; }
public UserAccount CreatedBy { get; set; }
}
public class UserAccount
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
This is what the resulting JSON would ideally look like:
[
{
"Id":1,
"Ask":"Question 1",
"CreatedBy":{
"Id":1
}
},
{
"Id":2,
"Ask":"Question 2",
"CreatedBy":{
"Id":1
}
},
{
"Id":3,
"Ask":"Question 3",
"CreatedBy":{
"Id":1
}
}
]
I don't want to use Ignore attributes against the UserAccount class, as in some other business cases, I might want all of the attributes serialised. In fact, I don't want to have to modify either the Question or UserAccount class.

You want to ignore the other properties conditionally. To achieve this, using NewtonJson you can simply add a method to your class next to the properties which has the following signiture (bool ShouldSerialize[PropertyName]), see the official documentation for more details.
So your code will look like this
public class UserAccount
{
// This will be serialized
public int Id { get; set; }
// This may (or may not) be be serialized depending on your condition
public string Name { get; set; }
// This also may (or may not) be be serialized depending on your condition
public string Email { get; set; }
public bool ShouldSerializeName()
{
if(someCondition)
{
return true;
}
else
{
return false;
}
}
public bool ShouldSerializeEmail()
{
if(someCondition)
{
return true;
}
else
{
return false;
}
}
}
If you don't want to edit your original classes, you may for example wrap them inside another class (or inherit from them), and use the derrived class. So you just inherit the properties and add the methods to the derrived class. This could be one possible solution (I am not sure if you might get a better solution).

if you are using NewtonSoft in order to do that you have to add "JsonIgnore" attribute to the prop(or "DataContract" to the class and "DataMember" to the prop) or implement IContractResolver
https://www.newtonsoft.com/json/help/html/ReducingSerializedJSONSize.htm

You can create a custom JsonConverter for your requirement and within the 'WriteJson' method, you can try the following implementation:
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;
var property = o.Properties().FirstOrDefault(p => p.Name == "CreatedBy");
if(property != null)
{
o.Remove(property.Name);
var newObj = new JObject();
newObj.Add(new JProperty("Id",((JObject)property.Value).Properties().First(p => p.Name == "Id").Value));
var newProperty = new JProperty(property.Name, newObj);
o.Add(newProperty);
o.WriteTo(writer);
}
}
}
And the serialization code would be like this:
var json = JsonConvert.SerializeObject(questions, // your list of 'Question'
Newtonsoft.Json.Formatting.Indented,
new MyCustomJsonConverter(typeof(Question)));

Related

JSON different serialization from deserialization in .NET [duplicate]

We have some configuration files which were generated by serializing C# objects with Json.net.
We'd like to migrate one property of the serialised class away from being a simple enum property into a class property.
One easy way to do this, would be to leave the old enum property on the class, and arrange for Json.net to read this property when we load the config, but not to save it again when we next serialize the object. We'll deal with generating the new class from the old enum separately.
Is there any simple way to mark (e.g. with attributes) a property of a C# object, so that Json.net will ignore it ONLY when serializing, but attend to it when deserializing?
There are actually several fairly simple approaches you can use to achieve the result you want.
Let's assume, for example, that you have your classes currently defined like this:
class Config
{
public Fizz ObsoleteSetting { get; set; }
public Bang ReplacementSetting { get; set; }
}
enum Fizz { Alpha, Beta, Gamma }
class Bang
{
public string Value { get; set; }
}
And you want to do this:
string json = #"{ ""ObsoleteSetting"" : ""Gamma"" }";
// deserialize
Config config = JsonConvert.DeserializeObject<Config>(json);
// migrate
config.ReplacementSetting =
new Bang { Value = config.ObsoleteSetting.ToString() };
// serialize
json = JsonConvert.SerializeObject(config);
Console.WriteLine(json);
To get this:
{"ReplacementSetting":{"Value":"Gamma"}}
Approach 1: Add a ShouldSerialize method
Json.NET has the ability to conditionally serialize properties by looking for corresponding ShouldSerialize methods in the class.
To use this feature, add a boolean ShouldSerializeBlah() method to your class where Blah is replaced with the name of the property that you do not want to serialize. Make the implementation of this method always return false.
class Config
{
public Fizz ObsoleteSetting { get; set; }
public Bang ReplacementSetting { get; set; }
public bool ShouldSerializeObsoleteSetting()
{
return false;
}
}
Note: if you like this approach but you don't want to muddy up the public interface of your class by introducing a ShouldSerialize method, you can use an IContractResolver to do the same thing programmatically. See Conditional Property Serialization in the documentation.
Approach 2: Manipulate the JSON with JObjects
Instead of using JsonConvert.SerializeObject to do the serialization, load the config object into a JObject, then simply remove the unwanted property from the JSON before writing it out. It's just a couple of extra lines of code.
JObject jo = JObject.FromObject(config);
// remove the "ObsoleteSetting" JProperty from its parent
jo["ObsoleteSetting"].Parent.Remove();
json = jo.ToString();
Approach 3: Clever (ab)use of attributes
Apply a [JsonIgnore] attribute to the property that you do not want to be serialized.
Add an alternate, private property setter to the class with the same type as the original property. Make the implementation of that property set the original property.
Apply a [JsonProperty] attribute to the alternate setter, giving it the same JSON name as the original property.
Here is the revised Config class:
class Config
{
[JsonIgnore]
public Fizz ObsoleteSetting { get; set; }
[JsonProperty("ObsoleteSetting")]
private Fizz ObsoleteSettingAlternateSetter
{
// get is intentionally omitted here
set { ObsoleteSetting = value; }
}
public Bang ReplacementSetting { get; set; }
}
For any situation where it's acceptable to have your deserialization-only property be marked internal, there's a remarkably simple solution that doesn't depend on attributes at all. Simply mark the property as internal get, but public set:
public class JsonTest {
public string SomeProperty { internal get; set; }
}
This results in correct deserialization using default settings/resolvers/etc., but the property is stripped from serialized output.
I like sticking with attributes on this one, here is the method I use when needing to deserialize a property but not serialize it or vice versa.
STEP 1 - Create the custom attribute
public class JsonIgnoreSerializationAttribute : Attribute { }
STEP 2 - Create a custom Contract Reslover
class JsonPropertiesResolver : DefaultContractResolver
{
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
//Return properties that do NOT have the JsonIgnoreSerializationAttribute
return objectType.GetProperties()
.Where(pi => !Attribute.IsDefined(pi, typeof(JsonIgnoreSerializationAttribute)))
.ToList<MemberInfo>();
}
}
STEP 3 - Add attribute where serialization is not needed but deserialization is
[JsonIgnoreSerialization]
public string Prop1 { get; set; } //Will be skipped when serialized
[JsonIgnoreSerialization]
public string Prop2 { get; set; } //Also will be skipped when serialized
public string Prop3 { get; set; } //Will not be skipped when serialized
STEP 4 - Use it
var sweet = JsonConvert.SerializeObject(myObj, new JsonSerializerSettings { ContractResolver = new JsonPropertiesResolver() });
Hope this helps! Also it's worth noting that this will also ignore the properties when Deserialization happens, when I am derserializing I just use the converter in the conventional way.
JsonConvert.DeserializeObject<MyType>(myString);
Use setter property:
[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { _ignoreOnSerializing = value; } }
[JsonIgnore]
private string _ignoreOnSerializing;
[JsonIgnore]
public string IgnoreOnSerializing
{
get { return this._ignoreOnSerializing; }
set { this._ignoreOnSerializing = value; }
}
Hope this help.
After i spent a quite long time searching how to flag a class property to be De-Serializable and NOT Serializable i found that there's no such thing to do that at all; so i came up with a solution that combines two different libraries or serialization techniques (System.Runtime.Serialization.Json & Newtonsoft.Json) and it worked for me like the following:
flag all your class and sub-classes as "DataContract".
flag all the properties of your class and sub-classes as "DataMember".
flag all the properties of your class and sub-classes as "JsonProperty" except those you want them not to be serialized.
now flag the properties the you do NOT want it to be serialized as "JsonIgnore".
then Serialize using "Newtonsoft.Json.JsonConvert.SerializeObject" and De-Serialize using "System.Runtime.Serialization.Json.DataContractJsonSerializer".
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Runtime.Serialization;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
namespace LUM_Win.model
{
[DataContract]
public class User
{
public User() { }
public User(String JSONObject)
{
MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(JSONObject));
DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(User));
User user = (User)dataContractJsonSerializer.ReadObject(stream);
this.ID = user.ID;
this.Country = user.Country;
this.FirstName = user.FirstName;
this.LastName = user.LastName;
this.Nickname = user.Nickname;
this.PhoneNumber = user.PhoneNumber;
this.DisplayPicture = user.DisplayPicture;
this.IsRegistred = user.IsRegistred;
this.IsConfirmed = user.IsConfirmed;
this.VerificationCode = user.VerificationCode;
this.Meetings = user.Meetings;
}
[DataMember(Name = "_id")]
[JsonProperty(PropertyName = "_id")]
public String ID { get; set; }
[DataMember(Name = "country")]
[JsonProperty(PropertyName = "country")]
public String Country { get; set; }
[DataMember(Name = "firstname")]
[JsonProperty(PropertyName = "firstname")]
public String FirstName { get; set; }
[DataMember(Name = "lastname")]
[JsonProperty(PropertyName = "lastname")]
public String LastName { get; set; }
[DataMember(Name = "nickname")]
[JsonProperty(PropertyName = "nickname")]
public String Nickname { get; set; }
[DataMember(Name = "number")]
[JsonProperty(PropertyName = "number")]
public String PhoneNumber { get; set; }
[DataMember(Name = "thumbnail")]
[JsonProperty(PropertyName = "thumbnail")]
public String DisplayPicture { get; set; }
[DataMember(Name = "registered")]
[JsonProperty(PropertyName = "registered")]
public bool IsRegistred { get; set; }
[DataMember(Name = "confirmed")]
[JsonProperty(PropertyName = "confirmed")]
public bool IsConfirmed { get; set; }
[JsonIgnore]
[DataMember(Name = "verification_code")]
public String VerificationCode { get; set; }
[JsonIgnore]
[DataMember(Name = "meeting_ids")]
public List<Meeting> Meetings { get; set; }
public String toJSONString()
{
return JsonConvert.SerializeObject(this, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
}
}
}
Hope that helps ...
Depending on where in the application this takes place and if it's just one property, one manual way you can do this is by setting the property value to null and then on the model you can specify that the property be ignored if the value is null:
[JsonProperty(NullValueHandling = NullValue.Ignore)]
public string MyProperty { get; set; }
If you are working on an ASP.NET Core web app, you can globally set this for all properties in all models by setting this in your Startup.cs file:
public void ConfigureServices(IServiceCollection services) {
// other configuration here
services.AddMvc()
.AddJsonOptions(options => options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore);
}
with reference to #ThoHo's solution, using the setter is actually all that is needed, with no additional tags.
For me I previously had a single reference Id, that I wanted to load and add to the new collection of reference Ids. By changing the definition of the reference Id to only contain a setter method, which added the value to the new collection. Json can't write the value back if the Property doesn't have a get; method.
// Old property that I want to read from Json, but never write again. No getter.
public Guid RefId { set { RefIds.Add(value); } }
// New property that will be in use from now on. Both setter and getter.
public ICollection<Guid> RefIds { get; set; }
This class is now backwards compatible with the previous version and only saves the RefIds for the new versions.
To build upon Tho Ho's answer, this can also be used for fields.
[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { IgnoreOnSerializing = value; } }
[JsonIgnore]
public string IgnoreOnSerializing;
If you use JsonConvert,IgnoreDataMemberAttribute is ok.My standard library not refrence Newton.Json,and I use [IgnoreDataMember] to control object serialize.
From Newton.net help document.
Is there any simple way to mark (e.g. with attributes) a property of a C# object, so that Json.net will ignore it ONLY when serializing, but attend to it when deserializing?
The easiest way I've found as of this writing is to include this logic in your IContractResolver.
Sample code from above link copied here for posterity:
public class Employee
{
public string Name { get; set; }
public Employee Manager { get; set; }
public bool ShouldSerializeManager()
{
// don't serialize the Manager property if an employee is their own manager
return (Manager != this);
}
}
public class ShouldSerializeContractResolver : DefaultContractResolver
{
public new static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.DeclaringType == typeof(Employee) && property.PropertyName == "Manager")
{
property.ShouldSerialize =
instance =>
{
Employee e = (Employee)instance;
return e.Manager != e;
};
}
return property;
}
}
All of the answers are good but this approach seemed like the cleanest way. I actually implemented this by looking for an attribute on the property for SkipSerialize and SkipDeserialize so you can just mark up any class you control. Great question!
Jraco11's answer is very neat. In case, if you want to use the same IContractResolver both for serialization and deserialization, then you can use the following:
public class JsonPropertiesResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (member.IsDefined(typeof(JsonIgnoreSerializationAttribute)))
{
property.ShouldSerialize = instance => false;
}
return property;
}
}
thats will do the trick, create a property with set only
example 1:
https://dotnetfiddle.net/IxMXcG
[JsonProperty("disabled-protections")]
public JArray DisabledProtections { set => IsPartialResult = (value != null && value.HasValues); }
public bool IsPartialResult { get; private set; }
example 2:
private JArray _disabledProtections;
[JsonProperty("disabled-protections")]
public JArray DisabledProtections { set => _disabledProtections = value; }
public bool IsPartialResult => _disabledProtections != null && _disabledProtections.HasValues;
Use [JsonIgnore] attribute in the public property of the model class.

Deserialize JSON to 2 different models

Does Newtonsoft.JSON library have a simple way I can automatically deserialize JSON into 2 different Models/classes?
For example I get the JSON:
[{
"guardian_id": "1453",
"guardian_name": "Foo Bar",
"patient_id": "938",
"patient_name": "Foo Bar",
}]
And I need de-serialize this to the following models:
class Guardian {
[JsonProperty(PropertyName = "guardian_id")]
public int ID { get; set; }
[JsonProperty(PropertyName = "guardian_name")]
public int Name { get; set; }
}
class Patient {
[JsonProperty(PropertyName = "patient_id")]
public int ID { get; set; }
[JsonProperty(PropertyName = "patient_name")]
public int Name { get; set; }
}
Is there a simple way to deserialize this JSON into 2 Models without having to iterate over the JSON? Maybe JSON property ids will just work?
Pair<Guardian, Patient> pair = JsonConvert.DeserializeObject(response.Content);
First off, your models are slightly incorrect. The name properties need to be strings, instead of integers:
class Guardian
{
[JsonProperty(PropertyName = "guardian_id")]
public int ID { get; set; }
[JsonProperty(PropertyName = "guardian_name")]
public string Name { get; set; } // <-- This
}
class Patient
{
[JsonProperty(PropertyName = "patient_id")]
public int ID { get; set; }
[JsonProperty(PropertyName = "patient_name")]
public string Name { get; set; } // <-- This
}
Once you've corrected that, you can deserialize the JSON string into two lists of different types. In your case, List<Guardian> and List<Patient> respectively:
string json = #"[{'guardian_id':'1453','guardian_name':'Foo Bar','patient_id':'938','patient_name':'Foo Bar'}]";
var guardians = JsonConvert.DeserializeObject<List<Guardian>>(json);
var patients = JsonConvert.DeserializeObject<List<Patient>>(json);
It you want to do it with 1 call, you need to create a class that matches the JSON. That class can then return Guardian and Patient objects as needed. Also you'll need to use an array or list for the return type because the source JSON is an array.
The class to create:
public class Pair
{
public Pair()
{
Guardian = new Guardian();
Patient = new Patient();
}
[JsonIgnore]
public Guardian Guardian { get; set; }
[JsonIgnore]
public Patient Patient { get; set; }
[JsonProperty(PropertyName = "guardian_id")]
public int GuardianID
{
get { return Guardian.ID; }
set { Guardian.ID = value; }
}
[JsonProperty(PropertyName = "guardian_name")]
public string GuardianName
{
get { return Guardian.Name; }
set { Guardian.Name = value; }
}
[JsonProperty(PropertyName = "patient_id")]
public int PatientID
{
get { return Patient.ID; }
set { Patient.ID = value; }
}
[JsonProperty(PropertyName = "patient_name")]
public string PatientName
{
get { return Patient.Name; }
set { Patient.Name = value; }
}
}
And how to use it:
var pairs = JsonConvert.DeserializeObject<Pair[]>(response.Content);
if (pairs.Any())
{
var pair = pairs[0];
Console.WriteLine(pair.Guardian.Name);
Console.WriteLine(pair.Patient.Name);
}
Not in one call, and it seems the data is an array, so you need a little more work.
Zip is the key method here to join the two separate object lists:
Guardian[] guardians = JsonConvert.DeserializeObject<Guardian[]>(response.Content);
Patient[] patients = JsonConvert.DeserializeObject<Patient[]>(response.Content);
var combined = guardians.Zip(patients, (g, p) => Tuple.Create(g, p)).ToList();
It would be far more easier to just read the JSON at once, it a single object.
It can't be done with 1 call with the types that you show. You can try using the generic <T> approach for each type, also you'll need to use arrays or lists for the return type because the source JSON is an array:
var guardians = JsonConvert.DeserializeObject<Guardian[]>(response.Content);
var patients = JsonConvert.DeserializeObject<Patient[]>(response.Content);
And then combine the two if you need them to be paired. E.g. if you are sure that you always have just one of each:
var pair = new Pair(guardians[0], patients[0]);
You could make a type to house the two subobjects:
[JsonConverter(typeof(GuardianPatientConverter))]
class GuardianPatient
{
public Guardian Guardian { get; set; }
public Patient Patient { get; set; }
}
And then create a JSON converter to handle the JSON:
class GuardianPatientConverter : JsonConverter
{
public override bool CanRead
{
get { return true; }
}
public override bool CanWrite
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
return typeof(GuardianPatient) == objectType;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
var jObject = JObject.Load(reader);
var guardian = new Guardian();
var patient = new Patient();
serializer.Populate(jObject.CreateReader(), guardian);
serializer.Populate(jObject.CreateReader(), patient);
return new GuardianPatient()
{
Guardian = guardian,
Patient = patient
};
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And then you can use it like so:
var json = "[{\"guardian_id\":\"1453\",\"guardian_name\":\"Foo Bar\",\"patient_id\":\"938\",\"patient_name\":\"Foo Bar\",}]";
var objects = JsonConvert.DeserializeObject<IEnumerable<GuardianPatient>>(json);
and if you want it as an array of pairs:
var objects = JsonConvert.DeserializeObject<IEnumerable<GuardianPatient>>(json)
.Select(o => new Pair(o.Guardian, o.Patient))
.ToArray();
This won't make it any faster, but I suspect you're looking for an easier way to work with the JSON.
One another approach would be creating class that matches JSON format, i.e. class with four properties with corresponding names. Then, deserialize JSON into that class and then use it in your code (set properties of objects with values from JSON, pass deserialized object to constructor of another class).
In your models, The name properties need to be strings, instead of integers. After correcting it.
You can use Tuple class
string json = #"[{'guardian_id':'1453','guardian_name':'Foo Bar','patient_id':'938','patient_name':'Foo Bar'}]";
var combination = new Tuple<List<Guardian>, List<Patient>>(JsonConvert.DeserializeObject<List<Guardian>>(json), JsonConvert.DeserializeObject<List<Patient>>(json));
static void Main(string[] args)
{
string json = JsonConvert.SerializeObject(new[]
{
new
{
guardian_id = "1453",
guardian_name = "Foo Bar",
patient_id = "938",
patient_name = "Bar Foo",
}
});
Guardian[] guardians = JsonConvert.DeserializeObject<Guardian[]>(json);
Patient[] patients = JsonConvert.DeserializeObject<Patient[]>(json);
}
Since both your objects are the same, wouldn't it make more sense to just have an ID/Name structure of a single base class? If you need to send all the data at the same time, you can restructure your data and use a data transfer object pattern. The JSON object would become
[{
"guardian": {
"id": "1453",
"name": "Foo Bar"
},
"patient": {
"id" : "938",
"name": "Foo Bar"
}
}]
And your corresponding data objects would be:
public class Record {
public int id { get; set; } // or string. I'm not sure which would be more appropriate
public string name { get; set;}
}
and
public class RecordDto {
public Record guardian { get; set; }
public Record patient { get; set; }
}
And your API would receive a
List<RecordDto>
parameter (since you are passing an array of objects).

Serializing a custom object using SOLID principles

I want to serialize my model objects (from WPF MVVM) which contains pure data. This sounds easy but I don't want to use the Serialization attributes and stuff provided in .NET framework. I just want to serialize it using my own way.
So here's a simplified version of one of my classes.
public class EntryKeyValuePair
{
public EntryKeyValuePair(string key, string value, bool isMultiline = false, bool isMandatory = true, bool isProtected = false)
{
Key = key;
Value = value;
IsMultiline = isMultiline;
IsMandatory = isMandatory;
IsProtected = isProtected;
}
public string Key { get; set; }
public string Value { get; set; }
public bool IsMultiline { get; set; }
public bool IsMandatory { get; set; }
public bool IsProtected { get; set; }
public static EntryKeyValuePair FromXML(XElement element, ICipher cipher)
{
string key = cipher.Decrypt(element.Element(nameof(Key)).Value);
string value = cipher.Decrypt(element.Element(nameof(Value)).Value);
bool isMultiline = bool.Parse(element.Element(nameof(IsMultiline)).Value);
bool isMandatory = bool.Parse(element.Element(nameof(IsMandatory)).Value);
bool isProtected = bool.Parse(element.Element(nameof(IsProtected)).Value);
return new EntryKeyValuePair(key, value, isMultiline, isMandatory, isProtected);
}
public XElement ToXML(ICipher cipher)
{
return new XElement(nameof(EntryKeyValuePair),
new XElement(nameof(Key),cipher.Encrypt(Key)),
new XElement(nameof(Value), cipher.Encrypt(Value)),
new XElement(nameof(IsMultiline), IsMultiline), new XElement(nameof(IsMandatory), IsMandatory),
new XElement(nameof(IsProtected), IsProtected));
}
}
This works quite well. But this violates single responsibility principle and maybe other principles as well. This is also difficult to maintain and extend.
So I wanted to find another way. And here it is:
First I defined an IStringFormatter interface which can format the data to any string data formats like XML and JSON. (Not sure tho)
interface IStringFormatter
{
string Name { get; set; }
Dictionary<string, string> FieldDictionary { get; }
string Format();
}
Here's how the XMLStringFormatter looks like:
class XmlStringFormatter : IStringFormatter
{
public XmlStringFormatter()
{
FieldDictionary = new Dictionary<string, string>();
}
public string Name { get; set; }
public Dictionary<string, string> FieldDictionary { get; }
public string Format()
{
var xElement = new XElement(Name, FieldDictionary.Keys.Select(key => new XElement(key, FieldDictionary[key])));
return xElement.ToString();
}
}
Then I defined an ISerializer to serialize (or rather save) my data objects to the IStringFormatter.
interface ISerializer<T>
{
T DeSerialize(IStringFormatter stringFormatter);
void Serialize(T obj, IStringFormatter stringFormatter);
}
And here is how I "Serialize" EntryKeyValurPair by implementing this:
internal class EntryKeyValurPairSerializer : ISerializer<EntryKeyValuePair>
{
public EntryKeyValuePair DeSerialize(IStringFormatter stringFormatter)
{
Dictionary<string, string> fieldDictionary = stringFormatter.FieldDictionary;
try {
string key = fieldDictionary[nameof(EntryKeyValuePair.Key)];
string value = fieldDictionary[nameof(EntryKeyValuePair.Value)];
bool isMandatory = bool.Parse(fieldDictionary[nameof(EntryKeyValuePair.IsMandatory)]);
bool isProtected = bool.Parse(fieldDictionary[nameof(EntryKeyValuePair.IsProtected)]);
bool isMultiline = bool.Parse(fieldDictionary[nameof(EntryKeyValuePair.IsMultiline)]);
return new EntryKeyValuePair(key, value, isMultiline, isMandatory, isProtected);
}
catch (KeyNotFoundException ex) {
throw new SerializationException(ex);
}
catch (FormatException ex) {
throw new SerializationException(ex);
}
}
public void Serialize(EntryKeyValuePair obj, IStringFormatter stringFormatter)
{
stringFormatter.Name = nameof(EntryKeyValuePair);
Dictionary<string, string> fieldDictionary = stringFormatter.FieldDictionary;
fieldDictionary.Add(nameof(EntryKeyValuePair.Key), obj.Key);
fieldDictionary.Add(nameof(EntryKeyValuePair.Value), obj.Value);
fieldDictionary.Add(nameof(EntryKeyValuePair.IsMandatory), obj.IsMandatory.ToString());
fieldDictionary.Add(nameof(EntryKeyValuePair.IsProtected), obj.IsProtected.ToString());
fieldDictionary.Add(nameof(EntryKeyValuePair.IsMultiline), obj.IsMultiline.ToString());
}
}
Now this works fine. But the problem is when I have a complex type like List<Entry> (where Entry is another data class) in my data classes.
As the IStringFormatter contains a Dictionary<string, string>, I can't just convert a List<Entry> to a string because I don't know what kind of IStringFormatter it wants in the context of ISerializer. How can I fix this? I also want to know if my solution is adhering to SOLID principles. If you can suggest a better solution (NOT the typical .NET serialization), I would appreciate it.
Writing your own serializer might be an interesting task, but I doubt that this is a good idea.
As I understood you want to keep your models clean, without any serialization specific attributes. I guess by "typical .NET serialization" you mean methods included with .Net framework.
For simplicity we will use these simple classes as an example:
class Customer
{
public string Name { get; set; }
public int Age { get; set; }
public List<Order> Orders { get; set; }
}
class Order
{
public int Id { get; set; }
public string Details { get; set; }
}
An easy option would be to use Json.NET:
var customer = new Customer
{
Name = "Darth Vader",
Age = 45,
Orders = new List<Order>
{
new Order { Id = 1, Details = "Order1" },
new Order { Id = 2, Details = "Order2" }
}
};
string json = JsonConvert.SerializeObject(customer);
So as you can see you don't need to add any custom attributes to Customer class. It will work until you want to serialize all public properties.
The resulting JSON will be:
{
"Name": "Darth Vader",
"Age": 45,
"Orders": [
{
"Id": 1,
"Details": "Order1"
},
{
"Id": 2,
"Details": "Order2"
}
]
}
After that you can always deserialize it:
var customer = JsonConvert.DeserializeObject<Customer>(json);
Lets say that you don't want Age property to be included. In this case I would suggest to create a different class that will be used for serialization only:
class CostomerSerializationContract
{
public string Name { get; set; }
public List<Order> Orders { get; set; }
}
This main advantage if this approach is that you have separate class for serialization, and you can add any custom attributes there, if you choose to use some other serializer, without violating SOLID principle. The main disadvantage is that you need to keep both objects in sync manually.
You can use AutoMapper to reduce manual work when creating serialization contract from source class.

Handling Master Data on (De-)Serialization with JSON.net

Using JSON.NET I am stuck on a issue with MasterData with regards to GET and POST operations.
Using the example of a movie, the JSON I could get on a GET operation before deserializing is the following:
{
“movie”: {
“name”:”bad boys”,
”genre”: {
“id”:"1",
”name”:”thriller”
}
}
}
However, on a POST operation I would like to construct a serialized string like the following:
{
“movie”: {
“name”:”bad boys”,
”genre”:"1"
}
}
As you can see, with a GET operation I need to deserialize the complete object, while on serialize for POST I need to add the Id only, but with the same JsonPropertyName.
What is the cleanest way to obtain this situation? I've been trying to work on the IContractResolver and IReferenceResolver, but those both didn't get me there yet. Also have been trying to work with propertynames as the following example:
[JsonProperty(PropertyName = "genre")]
public Genre Genre { get; set; }
[JsonProperty(PropertyName = "genre")]
public string GenreId { get { return Genre != null ? Genre.Id : 0; } }
I've also tried using the ShouldSerialize[MemberName] and ShouldDeserialize[MemberName] situations, but those gave an exception saying I cannot use two properties with the same name, which is logical in a way.
For now, my only idea left is to use a DTO for serialization, but I would rather prefer something more clean than that solution. So open for suggestions!
You can write a Custom converter.
public class GenreConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Genre);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return JObject.Load(reader).ToObject<Genre>();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var genre = value as Genre;
writer.WriteValue(genre.id);
}
}
All you need now is using JsonConverter atttribute
public class Movie
{
public string Name { get; set; }
[JsonConverter(typeof(GenreConverter))]
public Genre Genre { get; set; }
}
Use custom converter for this. You want to check type of genre attribute and read it as object or int.
In my opinion, using a DTO is the cleanest solution. Since your models are different for GET/POST, you ideally require two separate models. I've abused the NullValueHandling.Ignore serializer setting to mean you can use the same model for both situations, providing you don't mind always having the Id property on Genre:
class MovieJsonWrapper
{
[JsonProperty("movie")]
public Movie Movie { get; set; }
}
class Movie
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("genre")]
public Genre Genre { get; set; }
}
class Genre
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
We can then use this with:
var movie = new Movie
{
Name = "bad boys",
Genre = new Genre
{
Id = "1"
}
};
var movieJsonWrapper = new MovieJsonWrapper { Movie = movie };
var jsonSerializerSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };
var json = JsonConvert.SerializeObject(movieJsonWrapper, jsonSerializerSettings);
Which would produce the following:
{
"movie": {
"name": "bad boys",
"genre": {
"id": "1"
}
}
}
Similarly, we can then deserialize the GET response with:
var result = JsonConvert.DeserializeObject<MovieJsonWrapper>(raw);
If you really do need to ditch the Id property on the Genre when POSTing, then you would require additional Models. This solution reduces the amount of boilerplate.

Making a property deserialize but not serialize with json.net

We have some configuration files which were generated by serializing C# objects with Json.net.
We'd like to migrate one property of the serialised class away from being a simple enum property into a class property.
One easy way to do this, would be to leave the old enum property on the class, and arrange for Json.net to read this property when we load the config, but not to save it again when we next serialize the object. We'll deal with generating the new class from the old enum separately.
Is there any simple way to mark (e.g. with attributes) a property of a C# object, so that Json.net will ignore it ONLY when serializing, but attend to it when deserializing?
There are actually several fairly simple approaches you can use to achieve the result you want.
Let's assume, for example, that you have your classes currently defined like this:
class Config
{
public Fizz ObsoleteSetting { get; set; }
public Bang ReplacementSetting { get; set; }
}
enum Fizz { Alpha, Beta, Gamma }
class Bang
{
public string Value { get; set; }
}
And you want to do this:
string json = #"{ ""ObsoleteSetting"" : ""Gamma"" }";
// deserialize
Config config = JsonConvert.DeserializeObject<Config>(json);
// migrate
config.ReplacementSetting =
new Bang { Value = config.ObsoleteSetting.ToString() };
// serialize
json = JsonConvert.SerializeObject(config);
Console.WriteLine(json);
To get this:
{"ReplacementSetting":{"Value":"Gamma"}}
Approach 1: Add a ShouldSerialize method
Json.NET has the ability to conditionally serialize properties by looking for corresponding ShouldSerialize methods in the class.
To use this feature, add a boolean ShouldSerializeBlah() method to your class where Blah is replaced with the name of the property that you do not want to serialize. Make the implementation of this method always return false.
class Config
{
public Fizz ObsoleteSetting { get; set; }
public Bang ReplacementSetting { get; set; }
public bool ShouldSerializeObsoleteSetting()
{
return false;
}
}
Note: if you like this approach but you don't want to muddy up the public interface of your class by introducing a ShouldSerialize method, you can use an IContractResolver to do the same thing programmatically. See Conditional Property Serialization in the documentation.
Approach 2: Manipulate the JSON with JObjects
Instead of using JsonConvert.SerializeObject to do the serialization, load the config object into a JObject, then simply remove the unwanted property from the JSON before writing it out. It's just a couple of extra lines of code.
JObject jo = JObject.FromObject(config);
// remove the "ObsoleteSetting" JProperty from its parent
jo["ObsoleteSetting"].Parent.Remove();
json = jo.ToString();
Approach 3: Clever (ab)use of attributes
Apply a [JsonIgnore] attribute to the property that you do not want to be serialized.
Add an alternate, private property setter to the class with the same type as the original property. Make the implementation of that property set the original property.
Apply a [JsonProperty] attribute to the alternate setter, giving it the same JSON name as the original property.
Here is the revised Config class:
class Config
{
[JsonIgnore]
public Fizz ObsoleteSetting { get; set; }
[JsonProperty("ObsoleteSetting")]
private Fizz ObsoleteSettingAlternateSetter
{
// get is intentionally omitted here
set { ObsoleteSetting = value; }
}
public Bang ReplacementSetting { get; set; }
}
For any situation where it's acceptable to have your deserialization-only property be marked internal, there's a remarkably simple solution that doesn't depend on attributes at all. Simply mark the property as internal get, but public set:
public class JsonTest {
public string SomeProperty { internal get; set; }
}
This results in correct deserialization using default settings/resolvers/etc., but the property is stripped from serialized output.
I like sticking with attributes on this one, here is the method I use when needing to deserialize a property but not serialize it or vice versa.
STEP 1 - Create the custom attribute
public class JsonIgnoreSerializationAttribute : Attribute { }
STEP 2 - Create a custom Contract Reslover
class JsonPropertiesResolver : DefaultContractResolver
{
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
//Return properties that do NOT have the JsonIgnoreSerializationAttribute
return objectType.GetProperties()
.Where(pi => !Attribute.IsDefined(pi, typeof(JsonIgnoreSerializationAttribute)))
.ToList<MemberInfo>();
}
}
STEP 3 - Add attribute where serialization is not needed but deserialization is
[JsonIgnoreSerialization]
public string Prop1 { get; set; } //Will be skipped when serialized
[JsonIgnoreSerialization]
public string Prop2 { get; set; } //Also will be skipped when serialized
public string Prop3 { get; set; } //Will not be skipped when serialized
STEP 4 - Use it
var sweet = JsonConvert.SerializeObject(myObj, new JsonSerializerSettings { ContractResolver = new JsonPropertiesResolver() });
Hope this helps! Also it's worth noting that this will also ignore the properties when Deserialization happens, when I am derserializing I just use the converter in the conventional way.
JsonConvert.DeserializeObject<MyType>(myString);
Use setter property:
[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { _ignoreOnSerializing = value; } }
[JsonIgnore]
private string _ignoreOnSerializing;
[JsonIgnore]
public string IgnoreOnSerializing
{
get { return this._ignoreOnSerializing; }
set { this._ignoreOnSerializing = value; }
}
Hope this help.
After i spent a quite long time searching how to flag a class property to be De-Serializable and NOT Serializable i found that there's no such thing to do that at all; so i came up with a solution that combines two different libraries or serialization techniques (System.Runtime.Serialization.Json & Newtonsoft.Json) and it worked for me like the following:
flag all your class and sub-classes as "DataContract".
flag all the properties of your class and sub-classes as "DataMember".
flag all the properties of your class and sub-classes as "JsonProperty" except those you want them not to be serialized.
now flag the properties the you do NOT want it to be serialized as "JsonIgnore".
then Serialize using "Newtonsoft.Json.JsonConvert.SerializeObject" and De-Serialize using "System.Runtime.Serialization.Json.DataContractJsonSerializer".
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Runtime.Serialization;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
namespace LUM_Win.model
{
[DataContract]
public class User
{
public User() { }
public User(String JSONObject)
{
MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(JSONObject));
DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(User));
User user = (User)dataContractJsonSerializer.ReadObject(stream);
this.ID = user.ID;
this.Country = user.Country;
this.FirstName = user.FirstName;
this.LastName = user.LastName;
this.Nickname = user.Nickname;
this.PhoneNumber = user.PhoneNumber;
this.DisplayPicture = user.DisplayPicture;
this.IsRegistred = user.IsRegistred;
this.IsConfirmed = user.IsConfirmed;
this.VerificationCode = user.VerificationCode;
this.Meetings = user.Meetings;
}
[DataMember(Name = "_id")]
[JsonProperty(PropertyName = "_id")]
public String ID { get; set; }
[DataMember(Name = "country")]
[JsonProperty(PropertyName = "country")]
public String Country { get; set; }
[DataMember(Name = "firstname")]
[JsonProperty(PropertyName = "firstname")]
public String FirstName { get; set; }
[DataMember(Name = "lastname")]
[JsonProperty(PropertyName = "lastname")]
public String LastName { get; set; }
[DataMember(Name = "nickname")]
[JsonProperty(PropertyName = "nickname")]
public String Nickname { get; set; }
[DataMember(Name = "number")]
[JsonProperty(PropertyName = "number")]
public String PhoneNumber { get; set; }
[DataMember(Name = "thumbnail")]
[JsonProperty(PropertyName = "thumbnail")]
public String DisplayPicture { get; set; }
[DataMember(Name = "registered")]
[JsonProperty(PropertyName = "registered")]
public bool IsRegistred { get; set; }
[DataMember(Name = "confirmed")]
[JsonProperty(PropertyName = "confirmed")]
public bool IsConfirmed { get; set; }
[JsonIgnore]
[DataMember(Name = "verification_code")]
public String VerificationCode { get; set; }
[JsonIgnore]
[DataMember(Name = "meeting_ids")]
public List<Meeting> Meetings { get; set; }
public String toJSONString()
{
return JsonConvert.SerializeObject(this, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
}
}
}
Hope that helps ...
Depending on where in the application this takes place and if it's just one property, one manual way you can do this is by setting the property value to null and then on the model you can specify that the property be ignored if the value is null:
[JsonProperty(NullValueHandling = NullValue.Ignore)]
public string MyProperty { get; set; }
If you are working on an ASP.NET Core web app, you can globally set this for all properties in all models by setting this in your Startup.cs file:
public void ConfigureServices(IServiceCollection services) {
// other configuration here
services.AddMvc()
.AddJsonOptions(options => options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore);
}
with reference to #ThoHo's solution, using the setter is actually all that is needed, with no additional tags.
For me I previously had a single reference Id, that I wanted to load and add to the new collection of reference Ids. By changing the definition of the reference Id to only contain a setter method, which added the value to the new collection. Json can't write the value back if the Property doesn't have a get; method.
// Old property that I want to read from Json, but never write again. No getter.
public Guid RefId { set { RefIds.Add(value); } }
// New property that will be in use from now on. Both setter and getter.
public ICollection<Guid> RefIds { get; set; }
This class is now backwards compatible with the previous version and only saves the RefIds for the new versions.
To build upon Tho Ho's answer, this can also be used for fields.
[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { IgnoreOnSerializing = value; } }
[JsonIgnore]
public string IgnoreOnSerializing;
If you use JsonConvert,IgnoreDataMemberAttribute is ok.My standard library not refrence Newton.Json,and I use [IgnoreDataMember] to control object serialize.
From Newton.net help document.
Is there any simple way to mark (e.g. with attributes) a property of a C# object, so that Json.net will ignore it ONLY when serializing, but attend to it when deserializing?
The easiest way I've found as of this writing is to include this logic in your IContractResolver.
Sample code from above link copied here for posterity:
public class Employee
{
public string Name { get; set; }
public Employee Manager { get; set; }
public bool ShouldSerializeManager()
{
// don't serialize the Manager property if an employee is their own manager
return (Manager != this);
}
}
public class ShouldSerializeContractResolver : DefaultContractResolver
{
public new static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.DeclaringType == typeof(Employee) && property.PropertyName == "Manager")
{
property.ShouldSerialize =
instance =>
{
Employee e = (Employee)instance;
return e.Manager != e;
};
}
return property;
}
}
All of the answers are good but this approach seemed like the cleanest way. I actually implemented this by looking for an attribute on the property for SkipSerialize and SkipDeserialize so you can just mark up any class you control. Great question!
Jraco11's answer is very neat. In case, if you want to use the same IContractResolver both for serialization and deserialization, then you can use the following:
public class JsonPropertiesResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (member.IsDefined(typeof(JsonIgnoreSerializationAttribute)))
{
property.ShouldSerialize = instance => false;
}
return property;
}
}
thats will do the trick, create a property with set only
example 1:
https://dotnetfiddle.net/IxMXcG
[JsonProperty("disabled-protections")]
public JArray DisabledProtections { set => IsPartialResult = (value != null && value.HasValues); }
public bool IsPartialResult { get; private set; }
example 2:
private JArray _disabledProtections;
[JsonProperty("disabled-protections")]
public JArray DisabledProtections { set => _disabledProtections = value; }
public bool IsPartialResult => _disabledProtections != null && _disabledProtections.HasValues;
Use [JsonIgnore] attribute in the public property of the model class.

Categories