I have classes that 95% don't want to serialization some negation properties. So I use [JsonIgnore] attributes & Newtonsoft.Json, and it works fine.
However, I have only few methods want to return JSON that includes properties from [JsonIgnore]. How I can do that?
Thank you all
public class SubCatalog
{
[Key]
public int Id { get; set; }
[ForeignKey("Catalog")]
public int CatalogId { get; set; }
public string Name { get; set; }
[JsonIgnore]
public virtual Catalog Catalog { get; set; }
[JsonIgnore]
public virtual IList<Item> Items { get; set; }
}
With this method, I want to include all properties in entity.
public HttpResponseMessage GetSubCatalogs(int id)
{
var list = _uow.SubCatalogs.GetByCatalogId(id);
var json = JsonConvert.SerializeObject(list,
Formatting.Indented,
new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
return new HttpResponseMessage()
{
Content = new StringContent(json, System.Text.Encoding.UTF8, "application/json")
};
}
You may use the IContractResolver
public class IncludeIgnored : DefaultContractResolver
{
private readonly bool _includeIgnored;
public IncludeIgnored(bool includeIgnored)
{
_includeIgnored = includeIgnored;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
foreach (var item in properties)
{
item.Ignored = !_includeIgnored;
}
return properties;
}
}
Then when you call your serializer
var serializedObject= JsonConvert.SerializeObject(myObject, Formatting.Indented, new JsonSerializerSettings { ContractResolver = new IncludeIgnored(true) });
You can try to implement custom JsonConverter.
override WriteJson method and implement custom serialization there.
public class Converter : JsonConverter
{
public override bool CanRead { get { return false; } }
public override bool CanWrite { get { return true; } }
public override bool CanConvert(Type objectType)
{
return objectType == typeof(SubCatalog);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
//serialization code here
}
}
Add instance of this converter to Converters property of serializer or JsonSettings
Related
I have a class Employee where Manager property itself is of type Employee
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Employee Manager { get; set; }
public IList<string> Roles { get; set; }
}
I want to create a custom JsonConverter for Employee type.
public class TestJsonConverter : JsonConverter
{
public TestJsonConverter()
{
}
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<string> propertyNames = o.Properties().Select(p => p.Name).ToList();
o.AddFirst(new JProperty("Keys", new JArray(propertyNames)));
o.WriteTo(writer);
}
}
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 CanConvert(Type objectType)
{
return (objectType == typeof(Employee));
}
public override bool CanRead
{
get { return false; }
}
}
The ContractResolver is
class ContractResolver : DefaultContractResolver
{
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
JsonObjectContract contract = base.CreateObjectContract(objectType);
if (objectType == typeof(Employee))
{
contract.Converter = new TestJsonConverter();
}
return contract;
}
}
When I try to serialize the Employee object, custom JsonConverter is getting called only for the top level Employee object, not for the nested Manager property which is also of Employee type:
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.ContractResolver = new ContractResolver();
string json = JsonConvert.SerializeObject(employee, Formatting.Indented, settings);
I think you run into the same issue described in
Custom JsonConverter WriteJson Does Not Alter Serialization of Sub-properties:
The reason that your converter is not getting applied to your child objects is because JToken.FromObject() uses a new instance of the serializer internally, which does not know about your converter.
The solution provided should be adaptable to your case.
So the JsonConverter runs once, starting at the root node.
You will need to navigate this JSon Object tree and update it yourself.
Not sure it this is what you intended to achieve but I've tried it myself and you can decide if this works for you or not.
public class TestJsonConverter : JsonConverter
{
public TestJsonConverter()
{
}
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 root = (JObject)t;
var stack = new Stack<JObject>();
stack.Push(root);
while (stack.Any())
{
var current = stack.Pop();
var propertyNames = current.Properties().Select(p => p.Name).ToArray();
current.AddFirst(new JProperty("Keys", new JArray(propertyNames)));
var nestedObjects = current.Properties().Where(p => p.Value.Type == JTokenType.Object).ToArray();
foreach (var nestedObj in nestedObjects)
{
stack.Push((JObject)nestedObj.Value);
}
}
root.WriteTo(writer);
}
}
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 CanConvert(Type objectType)
{
return (objectType == typeof(Employee));
}
public override bool CanRead
{
get { return false; }
}
}
I have the following class definitions:
public class Tag
{
public Guid? TagId { get; set; }
public string TagText { get; set; }
public DateTime CreatedOn { get; set; }
}
public class Wiki
{
public Guid? WikiId { get; set; }
public string WikiText { get; set; }
public string Title { get; set; }
public DateTime CreatedOn { get; set; }
public IEnumerable<Tag> Tags { get; set; }
}
From the database i get the following json Object:
{
"WikiId": "83981284-0AD3-4420-90AB-15E3BF6BD7B7",
"WikiText": "Text",
"Title": "Title",
"CreatedOn": "2017-08-07T09:16:06.0800000",
"Tags": [{}] // <-- here i would like to ignore the empty Tag object
}
When i do now a JsonConvert.DeserializeObject<Wiki>(json) i get a Wiki object with a list of 1 Tag with the values TagId: null, TagText: null and CreatedOn: "0001-01-01T00:00:00"
Is there a way to ignore the empty Tag object while deserializing? I have tried several JsonSerializerSettings but nothing helped.
You could use a custom JsonConverter to ignore the empty objects during deserialization. Something like this could work:
class IgnoreEmptyItemsConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsAssignableFrom(typeof(List<T>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
List<T> list = new List<T>();
JArray array = JArray.Load(reader);
foreach (JObject obj in array.Children<JObject>())
{
if (obj.HasValues)
{
list.Add(obj.ToObject<T>(serializer));
}
}
return list;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use the converter, just add a [JsonConverter] attribute to your Tags property like this:
public class Wiki
{
...
[JsonConverter(typeof(IgnoreEmptyItemsConverter<Tag>))]
public IEnumerable<Tag> Tags { get; set; }
}
Fiddle: https://dotnetfiddle.net/hrAFsh
You'll have to detect the empty tag objects post-conversion and remove them yourself. From the deserializer's perspective, {} is a perfectly valid and complete Tag object whose properties are all unset.
Something like the following should work (presuming C# 6):
Wiki wiki = JsonConvert.DeserializeObject<Wiki>(json);
wiki.Tags = Wiki.Tags?.Where(x => x.TagId.HasValue)?.ToList();
Thanks to #Brian-Rogers's brilliant answer, I was able to come up with a non-generic solution which will work on all collections instead of List only:
public class IgnoreEmptyArrayItemsConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
bool result = typeof(System.Collections.IEnumerable).IsAssignableFrom(objectType);
return result;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var tokenIndexesToRemove = new List<int>();
var array = JArray.Load(reader);
for (int i = 0; i < array.Count; i++)
{
var obj = array[i];
if (!obj.HasValues)
tokenIndexesToRemove.Add(i);
}
foreach (int index in tokenIndexesToRemove)
array.RemoveAt(index);
var result = array.ToObject(objectType, serializer);
return result;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Instead of looping through the objects, deserializing them and adding them to a hardcoded List collection one by one, this solution will just remove the faulting tokens from the JArray and let the library deserialize the whole array to the type it should be.
Usage:
public class Wiki
{
...
[JsonConverter(typeof(IgnoreEmptyItemsConverter))] // No generic parameter needed
public HashSet<Tag> Tags { get; set; }
}
Fiddle: https://dotnetfiddle.net/9FCcpD
Due to a schema change I need to control the deserialization of property b depending on the API version or client version from the controller level.
public class MyModel
{
public string a { get; set; }
// old: public string b { get; set; }
public string[] b { get; set; }
}
I'm looking to implement a custom converter to write string[] as a single string if the version is old.
When constructing the response, I'm deserializing a parent model and want to use the custom converter only on this one property.
public class ParentModel
{
public MyModel myModel { get; set; }
}
This means an attribute for b wouldn't work. How can I implant such a custom converter on demand for just one property (custom converters switch on by a type, not a property name)?
Check bellow Code
public class ParentModelJSONConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
throw new ApplicationException("Serializable only");
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
Dictionary<string, object> result = new Dictionary<string, object>();
ParentModel myobj = obj as ParentModel;
if (myobj != null)
{
// Add your conditions
result.Add("MyKeyName", myobj.myModel);
}
return result;
}
public override IEnumerable<Type> SupportedTypes
{
get { return new Type[] { typeof(ParentModel) }; }
}
}
To use above code
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[] { new ParentModelJSONConverter() });
String json = serializer.Serialize(objParentModel);
Ended up doing the following manual conversion. Since it's a schema change, the old model can be easily hardcoded:
public class CustomConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var o = (MyModel)value;
writer.WriteStartObject();
writer.WritePropertyName("a");
writer.WriteValue(o.a);
writer.WritePropertyName("b");
writer.WriteValue(o.b[0]);
writer.WriteEndObject();
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(MyModel);
}
//...
}
}
I am trying to deserialize a JSON string to a concrete class, which inherits from an abstract class, but I just can't get it working. I have googled and tried some solutions but they don't seem to work either.
This is what I have now:
abstract class AbstractClass { }
class ConcreteClass { }
public AbstractClass Decode(string jsonString)
{
JsonSerializerSettings jss = new JsonSerializerSettings();
jss.TypeNameHandling = TypeNameHandling.All;
return (AbstractClass)JsonConvert.DeserializeObject(jsonString, null, jss);
}
However, if I try to cast the resulting object, it just doesn't work.
The reason why I don't use DeserializeObject is that I have many concrete classes.
Any suggestions?
I am using Newtonsoft.Json
One may not want to use TypeNameHandling (because one wants more compact json or wants to use a specific name for the type variable other than "$type"). Meanwhile, the customCreationConverter approach will not work if one wants to deserialize the base class into any of multiple derived classes without knowing which one to use in advance.
An alternative is to use an int or other type in the base class and define a JsonConverter.
[JsonConverter(typeof(BaseConverter))]
abstract class Base
{
public int ObjType { get; set; }
public int Id { get; set; }
}
class DerivedType1 : Base
{
public string Foo { get; set; }
}
class DerivedType2 : Base
{
public string Bar { get; set; }
}
The JsonConverter for the base class can then deserialize the object based on its type. The complication is that to avoid a stack overflow (where the JsonConverter repeatedly calls itself), a custom contract resolver must be used during this deserialization.
public class BaseSpecifiedConcreteClassConverter : DefaultContractResolver
{
protected override JsonConverter ResolveContractConverter(Type objectType)
{
if (typeof(Base).IsAssignableFrom(objectType) && !objectType.IsAbstract)
return null; // pretend TableSortRuleConvert is not specified (thus avoiding a stack overflow)
return base.ResolveContractConverter(objectType);
}
}
public class BaseConverter : JsonConverter
{
static JsonSerializerSettings SpecifiedSubclassConversion = new JsonSerializerSettings() { ContractResolver = new BaseSpecifiedConcreteClassConverter() };
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Base));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
switch (jo["ObjType"].Value<int>())
{
case 1:
return JsonConvert.DeserializeObject<DerivedType1>(jo.ToString(), SpecifiedSubclassConversion);
case 2:
return JsonConvert.DeserializeObject<DerivedType2>(jo.ToString(), SpecifiedSubclassConversion);
default:
throw new Exception();
}
throw new NotImplementedException();
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException(); // won't be called because CanWrite returns false
}
}
That's it. Now you can use serialize/deserialize any derived class. You can also use the base class in other classes and serialize/deserialize those without any additional work:
class Holder
{
public List<Base> Objects { get; set; }
}
string json = #"
[
{
""Objects"" :
[
{ ""ObjType"": 1, ""Id"" : 1, ""Foo"" : ""One"" },
{ ""ObjType"": 1, ""Id"" : 2, ""Foo"" : ""Two"" },
]
},
{
""Objects"" :
[
{ ""ObjType"": 2, ""Id"" : 3, ""Bar"" : ""Three"" },
{ ""ObjType"": 2, ""Id"" : 4, ""Bar"" : ""Four"" },
]
},
]";
List<Holder> list = JsonConvert.DeserializeObject<List<Holder>>(json);
string serializedAgain = JsonConvert.SerializeObject(list);
Debug.WriteLine(serializedAgain);
I would suggest to use CustomCreationConverter in the following way:
public enum ClassDiscriminatorEnum
{
ChildClass1,
ChildClass2
}
public abstract class BaseClass
{
public abstract ClassDiscriminatorEnum Type { get; }
}
public class Child1 : BaseClass
{
public override ClassDiscriminatorEnum Type => ClassDiscriminatorEnum.ChildClass1;
public int ExtraProperty1 { get; set; }
}
public class Child2 : BaseClass
{
public override ClassDiscriminatorEnum Type => ClassDiscriminatorEnum.ChildClass2;
}
public class BaseClassConverter : CustomCreationConverter<BaseClass>
{
private ClassDiscriminatorEnum _currentObjectType;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jobj = JObject.ReadFrom(reader);
_currentObjectType = jobj["Type"].ToObject<ClassDiscriminatorEnum>();
return base.ReadJson(jobj.CreateReader(), objectType, existingValue, serializer);
}
public override BaseClass Create(Type objectType)
{
switch (_currentObjectType)
{
case ClassDiscriminatorEnum.ChildClass1:
return new Child1();
case ClassDiscriminatorEnum.ChildClass2:
return new Child2();
default:
throw new NotImplementedException();
}
}
}
try something like this
public AbstractClass Decode(string jsonString)
{
var jss = new JavaScriptSerializer();
return jss.Deserialize<ConcreteClass>(jsonString);
}
UPDATE
for this scenario methinks all work as you want
public abstract class Base
{
public abstract int GetInt();
}
public class Der:Base
{
int g = 5;
public override int GetInt()
{
return g+2;
}
}
public class Der2 : Base
{
int i = 10;
public override int GetInt()
{
return i+17;
}
}
....
var jset = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All };
Base b = new Der()
string json = JsonConvert.SerializeObject(b, jset);
....
Base c = (Base)JsonConvert.DeserializeObject(json, jset);
where c type is test.Base {test.Der}
UPDATE
#Gusman suggest use TypeNameHandling.Objects instead of TypeNameHandling.All. It is enough and it will produce a less verbose serialization.
Actually, as it has been stated in an update, the simplest way (in 2019) is to use a simple custom pre-defined JsonSerializerSettings, as explained here
string jsonTypeNameAll = JsonConvert.SerializeObject(priceModels, Formatting.Indented,new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
});
And for deserializing :
TDSPriceModels models = JsonConvert.DeserializeObject<TDSPriceModels>(File.ReadAllText(jsonPath), new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
});
public class CustomConverter : JsonConverter
{
private static readonly JsonSerializer Serializer = new JsonSerializer();
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jObject = JObject.Load(reader);
var typeString = jObject.Value<string>("Kind"); //Kind is a property in json , from which we know type of child classes
var requiredType = RecoverType(typeString);
return Serializer.Deserialize(jObject.CreateReader(), requiredType);
}
private Type RecoverType(string typeString)
{
if (typeString.Equals(type of child class1, StringComparison.OrdinalIgnoreCase))
return typeof(childclass1);
if (typeString.Equals(type of child class2, StringComparison.OrdinalIgnoreCase))
return typeof(childclass2);
throw new ArgumentException("Unrecognized type");
}
public override bool CanConvert(Type objectType)
{
return typeof(Base class).IsAssignableFrom(objectType) || typeof((Base class) == objectType;
}
public override bool CanWrite { get { return false; } }
}
Now add this converter in JsonSerializerSettings as below
var jsonSerializerSettings = new JsonSerializerSettings();
jsonSerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
jsonSerializerSettings.Converters.Add(new CustomConverter());
After adding serialize or deserialize base class object as below
JsonConvert.DeserializeObject<Type>("json string", jsonSerializerSettings );
I had a similar issue, and I solved it with another way, maybe this would help someone:
I have json that contains in it several fields that are always the same, except for one field called "data" that can be a different type of class every time.
I would like to de-serialize it without analayzing every filed specific.
My solution is:
To define the main class (with 'Data' field) with , the field Data is type T.
Whenever that I de-serialize, I specify the type:
MainClass:
public class MainClass<T>
{
[JsonProperty("status")]
public Statuses Status { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("data")]
public T Data { get; set; }
public static MainClass<T> Parse(string mainClsTxt)
{
var response = JsonConvert.DeserializeObject<MainClass<T>>(mainClsTxt);
return response;
}
}
User
public class User
{
[JsonProperty("id")]
public int UserId { get; set; }
[JsonProperty("first_name")]
public string FirstName { get; set; }
[JsonProperty("last_name")]
public string LastName { get; set; }
}
Product
public class Product
{
[JsonProperty("product_id")]
public int ProductId { get; set; }
[JsonProperty("product_name")]
public string ProductName { get; set; }
[JsonProperty("stock")]
public int Stock { get; set; }
}
Using
var v = MainClass<User>.Parse(userJson);
var v2 = MainClass<Product>.Parse(productJson);
json example
userJson: "{"status":1,"description":"my description","data":{"id":12161347,"first_name":"my fname","last_name":"my lname"}}"
productJson: "{"status":1,"description":"my description","data":{"product_id":5,"product_name":"my product","stock":1000}}"
public abstract class JsonCreationConverter<T> : JsonConverter
{
protected abstract T Create(Type objectType, JObject jObject);
public override bool CanConvert(Type objectType)
{
return typeof(T) == objectType;
}
public override object ReadJson(JsonReader reader,Type objectType,
object existingValue, JsonSerializer serializer)
{
try
{
var jObject = JObject.Load(reader);
var target = Create(objectType, jObject);
serializer.Populate(jObject.CreateReader(), target);
return target;
}
catch (JsonReaderException)
{
return null;
}
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Now implement this interface
public class SportActivityConverter : JsonCreationConverter<BaseSportActivity>
{
protected override BaseSportActivity Create(Type objectType, JObject jObject)
{
BaseSportActivity result = null;
try
{
switch ((ESportActivityType)jObject["activityType"].Value<int>())
{
case ESportActivityType.Football:
result = jObject.ToObject<FootballActivity>();
break;
case ESportActivityType.Basketball:
result = jObject.ToObject<BasketballActivity>();
break;
}
}
catch(Exception ex)
{
Debug.WriteLine(ex);
}
return result;
}
}
I have a lot of entities which previously used to have properties of type for example string. I need to change these to a custom type - MultilingualValue<T>, where T in this case would be string. I can easily convert from string to the custom Type. Is it possible to configure JSON.Net such that everywhere it encounters a conversion from any type, to a MultilingualValue<T>, some custom code is called rather than it's native conversion?
Sample code
public class ProductBefore
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ProductAfter
{
public int Id { get; set; }
public MultilingualValue<string> Name { get; set; }
}
I would like to be able to deserialize anything which was stored as ProductBefore, into ProductAfter automatically. The MultilingualValue<string> can be initialised with a string parameter in the constructor, so it's relatively easy to create it from the original string.
It is possible through adding a Custom Converter. These can be added as per below:
private void createJsonSerializer()
{
JsonSerializerSettings settingsSerialize = new JsonSerializerSettings();
settingsSerialize.Converters.Add(CustomConverter.Instance);
_jsonSeriazlier = Newtonsoft.Json.JsonSerializer.Create(settingsSerialize);
}
Just in case it is of help to anyone, below is the custom converter itself:
public class CustomConverter : JsonConverter
{
public CustomConverter()
{
}
public override bool CanWrite
{
get { return false; }
}
private static readonly CustomConverter _instance = new CustomConverter();
public static CustomConverter Instance
{
get { return _instance; }
}
public override bool CanConvert(Type objectType)
{
return (objectType.IsAssignableFrom(typeof(MultilingualValue<MultilingualValueMetaData<string>>)));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
MultilingualValue<MultilingualValueMetaData<string>> result = new MultilingualValue<MultilingualValueMetaData<string>>();
JsonToken firstToken = reader.TokenType;
reader.Read();//skip first Token
while (reader.TokenType != JsonToken.EndObject)
{
string languageType = (string) reader.Value;
reader.Read();
MultilingualValueMetaData<string> metaData = null;
if (reader.TokenType == JsonToken.StartObject)
{
metaData = serializer.Deserialize<MultilingualValueMetaData<string>>(reader);
}
else
{
metaData = new MultilingualValueMetaData<string>();
metaData.AutoTranslation = (string) reader.Value;
}
result[languageType] = metaData;
reader.Read();
}
return result;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
}
}