Related
I would like to serialize this code via json.net:
public interface ITestInterface
{
string Guid {get;set;}
}
public class TestClassThatImplementsTestInterface1
{
public string Guid { get;set; }
}
public class TestClassThatImplementsTestInterface2
{
public string Guid { get;set; }
}
public class ClassToSerializeViaJson
{
public ClassToSerializeViaJson()
{
this.CollectionToSerialize = new List<ITestInterface>();
this.CollectionToSerialize.add( new TestClassThatImplementsTestInterface2() );
this.CollectionToSerialize.add( new TestClassThatImplementsTestInterface2() );
}
List<ITestInterface> CollectionToSerialize { get;set; }
}
I want to serialize/deserialize ClassToSerializeViaJson with json.net. Serialization is working, but deserialization gives me this error:
Newtonsoft.Json.JsonSerializationException: Could not create an instance of type ITestInterface. Type is an interface or abstract class and cannot be instantiated.
So how can I deserialize the List<ITestInterface> collection?
I found this question while trying to do this myself. After I implemented Piotr Stapp's(Garath's) answer, I was struck by how simple it seemed. If I was merely implementing a method that was already being passed the exact Type (as a string) that I wanted to instantiate, why wasn't the library binding it automatically?
I actually found that I didn't need any custom binders, Json.Net was able to do exactly what I needed, provided I told it that was what I was doing.
When serializing:
string serializedJson = JsonConvert.SerializeObject(objectToSerialize, Formatting.Indented, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple
});
When de-serializing:
var deserializedObject = JsonConvert.DeserializeObject<ClassToSerializeViaJson>(serializedJson, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects
});
Relevant documentation: Serialization Settings for Json.NET and TypeNameHandling setting
Bellow full working example with what you want to do:
public interface ITestInterface
{
string Guid { get; set; }
}
public class TestClassThatImplementsTestInterface1 : ITestInterface
{
public string Guid { get; set; }
public string Something1 { get; set; }
}
public class TestClassThatImplementsTestInterface2 : ITestInterface
{
public string Guid { get; set; }
public string Something2 { get; set; }
}
public class ClassToSerializeViaJson
{
public ClassToSerializeViaJson()
{
this.CollectionToSerialize = new List<ITestInterface>();
}
public List<ITestInterface> CollectionToSerialize { get; set; }
}
public class TypeNameSerializationBinder : SerializationBinder
{
public string TypeFormat { get; private set; }
public TypeNameSerializationBinder(string typeFormat)
{
TypeFormat = typeFormat;
}
public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
assemblyName = null;
typeName = serializedType.Name;
}
public override Type BindToType(string assemblyName, string typeName)
{
var resolvedTypeName = string.Format(TypeFormat, typeName);
return Type.GetType(resolvedTypeName, true);
}
}
class Program
{
static void Main()
{
var binder = new TypeNameSerializationBinder("ConsoleApplication.{0}, ConsoleApplication");
var toserialize = new ClassToSerializeViaJson();
toserialize.CollectionToSerialize.Add(
new TestClassThatImplementsTestInterface1()
{
Guid = Guid.NewGuid().ToString(), Something1 = "Some1"
});
toserialize.CollectionToSerialize.Add(
new TestClassThatImplementsTestInterface2()
{
Guid = Guid.NewGuid().ToString(), Something2 = "Some2"
});
string json = JsonConvert.SerializeObject(toserialize, Formatting.Indented,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
Binder = binder
});
var obj = JsonConvert.DeserializeObject<ClassToSerializeViaJson>(json,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
Binder = binder
});
Console.ReadLine();
}
}
I was also surprised by the simplicity in Garath's, and also came to the conclusion that the Json library can do it automatically. But I also figured that it's even simpler than Ben Jenkinson's answer (even though I can see it has been modified by the developer of the json library himself). From my testings, all you need to do is set TypeNameHandling to Auto, like this:
var objectToSerialize = new List<IFoo>();
// TODO: Add objects to list
var jsonString = JsonConvert.SerializeObject(objectToSerialize,
new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
var deserializedObject = JsonConvert.DeserializeObject<List<IFoo>>(jsonString,
new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
From TypeNameHandling Enumeration documentation
Auto: Include the .NET type name when the type of the object being
serialized is not the same as its declared type. Note that this
doesn't include the root serialized object by default.
Using the default settings, you cannot. JSON.NET has no way of knowing how to deserialize an array. However, you can specify which type converter to use for your interface type. To see how to do this, see this page: http://blog.greatrexpectations.com/2012/08/30/deserializing-interface-properties-using-json-net/
You can also find information about this problem at this SO question: Casting interfaces for deserialization in JSON.NET
This is an old question, but thought I'd add a more in-depth answer (in the form of an article I wrote): http://skrift.io/articles/archive/bulletproof-interface-deserialization-in-jsonnet/
TLDR: Rather than configure Json.NET to embed type names in the serialized JSON, you can use a JSON converter to figure out which class to deserialize to using whatever custom logic you like.
This has the advantage that you can refactor your types without worrying about deserialization breaking.
It can be done with JSON.NET and JsonSubTypes attributes:
[JsonConverter(typeof(JsonSubtypes))]
[JsonSubtypes.KnownSubTypeWithProperty(typeof(Test1), "Something1")]
[JsonSubtypes.KnownSubTypeWithProperty(typeof(Test2), "Something2")]
public interface ITestInterface
{
string Guid { get; set; }
}
public class Test1 : ITestInterface
{
public string Guid { get; set; }
public string Something1 { get; set; }
}
public class Test2 : ITestInterface
{
public string Guid { get; set; }
public string Something2 { get; set; }
}
and simply:
var fromCode = new List<ITestInterface>();
// TODO: Add objects to list
var json = JsonConvert.SerializeObject(fromCode);
var fromJson = JsonConvert.DeserializeObject<List<ITestInterface>>(json);
I wanted to deserialize JSON that wasn't serialized by my application, hence I needed to specify the concrete implementation manually. I have expanded on Nicholas's answer.
Lets say we have
public class Person
{
public ILocation Location { get;set; }
}
and the concrete instance of
public class Location: ILocation
{
public string Address1 { get; set; }
// etc
}
Add in this class
public class ConfigConverter<I, T> : JsonConverter
{
public override bool CanWrite => false;
public override bool CanRead => true;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(I);
}
public override void WriteJson(JsonWriter writer,
object value, JsonSerializer serializer)
{
throw new InvalidOperationException("Use default serialization.");
}
public override object ReadJson(JsonReader reader,
Type objectType, object existingValue,
JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var deserialized = (T)Activator.CreateInstance(typeof(T));
serializer.Populate(jsonObject.CreateReader(), deserialized);
return deserialized;
}
}
Then define your interfaces with the JsonConverter attribute
public class Person
{
[JsonConverter(typeof(ConfigConverter<ILocation, Location>))]
public ILocation Location { get;set; }
}
Near-duplicate of Inrego's answer, but it's worthy of further explanation:
If you use TypeNameHandling.Auto then it only includes the type/assembly name when it needs to (i.e. interfaces and base/derived classes). So your JSON is cleaner, smaller, more specific.
Which isn't that one of the main selling points of it over XML/SOAP?
Avoid TypeNameHandling.Auto when possible, particularly with user-controllable values.
You will need to write your own deserializer for the collection type.
Rather than repeat others who have already posted boilerplate converter code (particularly Nicholas Westby, whose blog post was quite useful and is linked above), I have included the relevant changes for deserializing a collection of interfaces (I had an enum interface property to distinguish implementors):
public override object ReadJson(JsonReader reader,
Type objectType, object existingValue,
JsonSerializer serializer)
{
Collection<T> result = new Collection<T>();
var array = JArray.Load(reader);
foreach (JObject jsonObject in array)
{
var rule = default(T);
var value = jsonObject.Value<string>("MyDistinguisher");
MyEnum distinguisher;
Enum.TryParse(value, out distinguisher);
switch (distinguisher)
{
case MyEnum.Value1:
rule = serializer.Deserialize<Type1>(jsonObject.CreateReader());
break;
case MyEnum.Value2:
rule = serializer.Deserialize<Type2>(jsonObject.CreateReader());
break;
default:
rule = serializer.Deserialize<Type3>(jsonObject.CreateReader());
break;
}
result.Add(rule);
}
return result;
}
I hope this is helpful to the next person looking for an interface collection deserializer.
I'm trying to have some sort of attribute that I can add on a model class so that during json deserialization, if a key is a certain format (in this case, ends with _special), it will be added to a list on the model.
ie:
C#
public class MyModel
{
public string Name { get; set; }
public IDictionary<string, string> SpecialFields { get; set; }
}
JSON
{
"Name": "John",
"Height_special": "72",
"Weight_special": "200"
}
So the result would be: MyModel.Name == "John" and MyModel.SpecialFields would be the key value pairs of height and weight.
Is this possible?
EDIT: Project is using .NET Core 3.1 and System.Text.Json
You can do this with a custom JSON converter for System.Text.Json. Here's an example that is extensible, and fits your specific MyModel class:
public class MyModelJsonConverter : JsonConverter<MyModel>
{
public override MyModel Read(ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
MyModel model = new MyModel()
{
SpecialFields = new Dictionary<string, string>()
};
string nextPropertyName = string.Empty;
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonTokenType.PropertyName:
nextPropertyName = reader.GetString();
break;
case JsonTokenType.String:
// Tempting to use reflection or switch here, but the if/else-if performs faster
if (nextPropertyName == "Name")
{
model.Name = reader.GetString();
continue;
}
// Make sure it's a _special field; if not, it's unrecognized!
int nameEndsAtIndex = nextPropertyName.IndexOf("_special");
if (nameEndsAtIndex == -1) throw new InvalidDataException("Unknown JSON field");
string key = nextPropertyName.Substring(0, nameEndsAtIndex);
string value = reader.GetString();
model.SpecialFields.Add(key, value);
break;
}
}
return model;
}
public override void Write(Utf8JsonWriter writer,
MyModel value,
JsonSerializerOptions options)
{
writer.WritePropertyName("Name");
writer.WriteStringValue(value.Name);
foreach (var specialField in value.SpecialFields)
{
writer.WritePropertyName($"{specialField.Key}_special");
writer.WriteStringValue(specialField.Value);
}
}
}
This can be used as follows:
var options = new JsonSerializerOptions()
{
Converters = { new MyModelJsonConverter() }
};
var model = JsonSerializer.Deserialize<MyModel>(jsonString, options);
Note, this custom converter can round-trip your MyModel class:
var jsonString = JsonSerializer.Serialize<MyModel>(model, options);
In order to use this setup in ASP.NET Core application or Web API, you'll need to add an attribute to your model class, to let the ASP.NET Core framework know that you intend to use a custom converter:
[JsonConverter(typeof(MyModelJsonConverter))]
public class MyModel
{
public string Name { get; set; }
public IDictionary<string, string> SpecialFields { get; set; }
}
...and, just like that, the conversion will be handled automagically!
I strongly encourage you to step through the Read and Write methods with breakpoints, to really understand what's happening underneath. These methods will be called by the JsonSerializer directly; by understanding how these work, you can see how to extend this custom converter, should you have the need to do so.
It can be achieved with some steps:
Create a type for Json fields:
public class SpecialClass
{
public string Name { get; set; }
public string Height_Special { get; set; }
public string Weight_Special { get; set; }
}
Convert the Json with this type:
var myObject = JsonConvert.DeserializeObject<SpecialClass>(yourJson);
Create dictionary of _special fields as [height as key, weight as value]:
var dictionary = new Dictionary<string, string>()
{
{ myObject.Height_Special, myObject.Weight_Special }
};
If its required to get [key, value] as [propertyName, value]:
var dictionary = new Dictionary<string, string>()
{
{ nameof(myObject.Height_Special), myObject.Height_Special },
{ nameof(myObject.Weight_Special), myObject.Weight_Special }
};
So you can get your required data in MyModel type as:
MyModel myModel = new MyModel()
{
Name = myObject.Name,
SpecialFields = dictionary,
};
I have this class:
public class ValueInteger
{
[JsonIgnore]
public string ValueName { get; set; }
public int Value { get; set; }
[JsonProperty("timestamp")]
public UInt64 TimeStamp { get; set; }
}
Given an instance of:
var valueInt = new ValueInteger
{
ValueName = "mycounter",
Value = 7,
TimeStamp = 1010101010
}
It should serialize to:
{ mycounter: 7, timestamp = 1010101010 }
It would be cool if one could declare the Value property as
[JsonRedirect(titlePropertyName: nameof(ValueName))]
public int Value { get; set; }
I probably have to implement my own ContractResolver, and have studiet this post: https://stackoverflow.com/a/47872645/304820
but it depends on the IValueProvider, and AFAIK there is no INameProvider to use for renaming.
Usually renaming is done per class, not per instance.
My approach to this would be to write my own Converter. The converter simply serializes in the same fashion as a normal converter, but whenever it comes across a special attribute on a property, it should rename that property in the output.
So, serializing a C# object would go like this:
First convert the C# object into a JSON object (i.e. JTokens structure).
Run through the properties in the C# object and find the ones that need to be renamed...
For each of those properties, determine what their current name is and what their new name should be.
Perform the renaming on the JSON object.
Finally serialize the JSON object into a string.
I have made a simple implementation of this. The usage looks like this:
class Program
{
static void Main(string[] args)
{
var valueInt = new ValueInteger
{
ValueName = "mycounter",
Value = 7,
TimeStamp = 1010101010
};
var settings = new JsonSerializerSettings { Converters = new JsonConverter[] { new DynamicNameConverter() } };
var result = JsonConvert.SerializeObject(valueInt, settings);
Console.WriteLine(result);
Console.Read();
}
}
public class ValueInteger
{
[JsonIgnore]
public string ValueName { get; set; }
[JsonDynamicName(nameof(ValueName))]
public int Value { get; set; }
[JsonProperty("timestamp")]
public UInt64 TimeStamp { get; set; }
}
And the helper classes:
class DynamicNameConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
// Only use this converter for classes that contain properties with an JsonDynamicNameAttribute.
return objectType.IsClass && objectType.GetProperties().Any(prop => prop.CustomAttributes.Any(attr => attr.AttributeType == typeof(JsonDynamicNameAttribute)));
}
public override bool CanRead => false;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// We do not support deserialization.
throw new NotImplementedException();
}
public override bool CanWrite => true;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var token = JToken.FromObject(value);
if (token.Type != JTokenType.Object)
{
// We should never reach this point because CanConvert() only allows objects with JsonPropertyDynamicNameAttribute to pass through.
throw new Exception("Token to be serialized was unexpectedly not an object.");
}
JObject o = (JObject)token;
var propertiesWithDynamicNameAttribute = value.GetType().GetProperties().Where(
prop => prop.CustomAttributes.Any(attr => attr.AttributeType == typeof(JsonDynamicNameAttribute))
);
foreach (var property in propertiesWithDynamicNameAttribute)
{
var dynamicAttributeData = property.CustomAttributes.FirstOrDefault(attr => attr.AttributeType == typeof(JsonDynamicNameAttribute));
// Determine what we should rename the property from and to.
var currentName = property.Name;
var propertyNameContainingNewName = (string)dynamicAttributeData.ConstructorArguments[0].Value;
var newName = (string)value.GetType().GetProperty(propertyNameContainingNewName).GetValue(value);
// Perform the renaming in the JSON object.
var currentJsonPropertyValue = o[currentName];
var newJsonProperty = new JProperty(newName, currentJsonPropertyValue);
currentJsonPropertyValue.Parent.Replace(newJsonProperty);
}
token.WriteTo(writer);
}
}
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
class JsonDynamicNameAttribute : Attribute
{
public string ObjectPropertyName { get; }
public JsonDynamicNameAttribute(string objectPropertyName)
{
ObjectPropertyName = objectPropertyName;
}
}
Please be aware that a lot of error handling could be put into DynamicNameConverter but I have left it out to make it easier to read and understand.
I would like to serialize this code via json.net:
public interface ITestInterface
{
string Guid {get;set;}
}
public class TestClassThatImplementsTestInterface1
{
public string Guid { get;set; }
}
public class TestClassThatImplementsTestInterface2
{
public string Guid { get;set; }
}
public class ClassToSerializeViaJson
{
public ClassToSerializeViaJson()
{
this.CollectionToSerialize = new List<ITestInterface>();
this.CollectionToSerialize.add( new TestClassThatImplementsTestInterface2() );
this.CollectionToSerialize.add( new TestClassThatImplementsTestInterface2() );
}
List<ITestInterface> CollectionToSerialize { get;set; }
}
I want to serialize/deserialize ClassToSerializeViaJson with json.net. Serialization is working, but deserialization gives me this error:
Newtonsoft.Json.JsonSerializationException: Could not create an instance of type ITestInterface. Type is an interface or abstract class and cannot be instantiated.
So how can I deserialize the List<ITestInterface> collection?
I found this question while trying to do this myself. After I implemented Piotr Stapp's(Garath's) answer, I was struck by how simple it seemed. If I was merely implementing a method that was already being passed the exact Type (as a string) that I wanted to instantiate, why wasn't the library binding it automatically?
I actually found that I didn't need any custom binders, Json.Net was able to do exactly what I needed, provided I told it that was what I was doing.
When serializing:
string serializedJson = JsonConvert.SerializeObject(objectToSerialize, Formatting.Indented, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple
});
When de-serializing:
var deserializedObject = JsonConvert.DeserializeObject<ClassToSerializeViaJson>(serializedJson, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects
});
Relevant documentation: Serialization Settings for Json.NET and TypeNameHandling setting
Bellow full working example with what you want to do:
public interface ITestInterface
{
string Guid { get; set; }
}
public class TestClassThatImplementsTestInterface1 : ITestInterface
{
public string Guid { get; set; }
public string Something1 { get; set; }
}
public class TestClassThatImplementsTestInterface2 : ITestInterface
{
public string Guid { get; set; }
public string Something2 { get; set; }
}
public class ClassToSerializeViaJson
{
public ClassToSerializeViaJson()
{
this.CollectionToSerialize = new List<ITestInterface>();
}
public List<ITestInterface> CollectionToSerialize { get; set; }
}
public class TypeNameSerializationBinder : SerializationBinder
{
public string TypeFormat { get; private set; }
public TypeNameSerializationBinder(string typeFormat)
{
TypeFormat = typeFormat;
}
public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
assemblyName = null;
typeName = serializedType.Name;
}
public override Type BindToType(string assemblyName, string typeName)
{
var resolvedTypeName = string.Format(TypeFormat, typeName);
return Type.GetType(resolvedTypeName, true);
}
}
class Program
{
static void Main()
{
var binder = new TypeNameSerializationBinder("ConsoleApplication.{0}, ConsoleApplication");
var toserialize = new ClassToSerializeViaJson();
toserialize.CollectionToSerialize.Add(
new TestClassThatImplementsTestInterface1()
{
Guid = Guid.NewGuid().ToString(), Something1 = "Some1"
});
toserialize.CollectionToSerialize.Add(
new TestClassThatImplementsTestInterface2()
{
Guid = Guid.NewGuid().ToString(), Something2 = "Some2"
});
string json = JsonConvert.SerializeObject(toserialize, Formatting.Indented,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
Binder = binder
});
var obj = JsonConvert.DeserializeObject<ClassToSerializeViaJson>(json,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
Binder = binder
});
Console.ReadLine();
}
}
I was also surprised by the simplicity in Garath's, and also came to the conclusion that the Json library can do it automatically. But I also figured that it's even simpler than Ben Jenkinson's answer (even though I can see it has been modified by the developer of the json library himself). From my testings, all you need to do is set TypeNameHandling to Auto, like this:
var objectToSerialize = new List<IFoo>();
// TODO: Add objects to list
var jsonString = JsonConvert.SerializeObject(objectToSerialize,
new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
var deserializedObject = JsonConvert.DeserializeObject<List<IFoo>>(jsonString,
new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
From TypeNameHandling Enumeration documentation
Auto: Include the .NET type name when the type of the object being
serialized is not the same as its declared type. Note that this
doesn't include the root serialized object by default.
Using the default settings, you cannot. JSON.NET has no way of knowing how to deserialize an array. However, you can specify which type converter to use for your interface type. To see how to do this, see this page: http://blog.greatrexpectations.com/2012/08/30/deserializing-interface-properties-using-json-net/
You can also find information about this problem at this SO question: Casting interfaces for deserialization in JSON.NET
This is an old question, but thought I'd add a more in-depth answer (in the form of an article I wrote): http://skrift.io/articles/archive/bulletproof-interface-deserialization-in-jsonnet/
TLDR: Rather than configure Json.NET to embed type names in the serialized JSON, you can use a JSON converter to figure out which class to deserialize to using whatever custom logic you like.
This has the advantage that you can refactor your types without worrying about deserialization breaking.
It can be done with JSON.NET and JsonSubTypes attributes:
[JsonConverter(typeof(JsonSubtypes))]
[JsonSubtypes.KnownSubTypeWithProperty(typeof(Test1), "Something1")]
[JsonSubtypes.KnownSubTypeWithProperty(typeof(Test2), "Something2")]
public interface ITestInterface
{
string Guid { get; set; }
}
public class Test1 : ITestInterface
{
public string Guid { get; set; }
public string Something1 { get; set; }
}
public class Test2 : ITestInterface
{
public string Guid { get; set; }
public string Something2 { get; set; }
}
and simply:
var fromCode = new List<ITestInterface>();
// TODO: Add objects to list
var json = JsonConvert.SerializeObject(fromCode);
var fromJson = JsonConvert.DeserializeObject<List<ITestInterface>>(json);
I wanted to deserialize JSON that wasn't serialized by my application, hence I needed to specify the concrete implementation manually. I have expanded on Nicholas's answer.
Lets say we have
public class Person
{
public ILocation Location { get;set; }
}
and the concrete instance of
public class Location: ILocation
{
public string Address1 { get; set; }
// etc
}
Add in this class
public class ConfigConverter<I, T> : JsonConverter
{
public override bool CanWrite => false;
public override bool CanRead => true;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(I);
}
public override void WriteJson(JsonWriter writer,
object value, JsonSerializer serializer)
{
throw new InvalidOperationException("Use default serialization.");
}
public override object ReadJson(JsonReader reader,
Type objectType, object existingValue,
JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var deserialized = (T)Activator.CreateInstance(typeof(T));
serializer.Populate(jsonObject.CreateReader(), deserialized);
return deserialized;
}
}
Then define your interfaces with the JsonConverter attribute
public class Person
{
[JsonConverter(typeof(ConfigConverter<ILocation, Location>))]
public ILocation Location { get;set; }
}
Near-duplicate of Inrego's answer, but it's worthy of further explanation:
If you use TypeNameHandling.Auto then it only includes the type/assembly name when it needs to (i.e. interfaces and base/derived classes). So your JSON is cleaner, smaller, more specific.
Which isn't that one of the main selling points of it over XML/SOAP?
Avoid TypeNameHandling.Auto when possible, particularly with user-controllable values.
You will need to write your own deserializer for the collection type.
Rather than repeat others who have already posted boilerplate converter code (particularly Nicholas Westby, whose blog post was quite useful and is linked above), I have included the relevant changes for deserializing a collection of interfaces (I had an enum interface property to distinguish implementors):
public override object ReadJson(JsonReader reader,
Type objectType, object existingValue,
JsonSerializer serializer)
{
Collection<T> result = new Collection<T>();
var array = JArray.Load(reader);
foreach (JObject jsonObject in array)
{
var rule = default(T);
var value = jsonObject.Value<string>("MyDistinguisher");
MyEnum distinguisher;
Enum.TryParse(value, out distinguisher);
switch (distinguisher)
{
case MyEnum.Value1:
rule = serializer.Deserialize<Type1>(jsonObject.CreateReader());
break;
case MyEnum.Value2:
rule = serializer.Deserialize<Type2>(jsonObject.CreateReader());
break;
default:
rule = serializer.Deserialize<Type3>(jsonObject.CreateReader());
break;
}
result.Add(rule);
}
return result;
}
I hope this is helpful to the next person looking for an interface collection deserializer.
I have a DTO class which I Serialize
Json.Serialize(MyClass)
How can I exclude a public property of it?
(It has to be public, as I use it in my code somewhere else)
If you are using Json.Net attribute [JsonIgnore] will simply ignore the field/property while serializing or deserialising.
public class Car
{
// included in JSON
public string Model { get; set; }
public DateTime Year { get; set; }
public List<string> Features { get; set; }
// ignored
[JsonIgnore]
public DateTime LastModified { get; set; }
}
Or you can use DataContract and DataMember attribute to selectively serialize/deserialize properties/fields.
[DataContract]
public class Computer
{
// included in JSON
[DataMember]
public string Name { get; set; }
[DataMember]
public decimal SalePrice { get; set; }
// ignored
public string Manufacture { get; set; }
public int StockCount { get; set; }
public decimal WholeSalePrice { get; set; }
public DateTime NextShipmentDate { get; set; }
}
Refer http://james.newtonking.com/archive/2009/10/23/efficient-json-with-json-net-reducing-serialized-json-size for more details
If you are using System.Web.Script.Serialization in the .NET framework you can put a ScriptIgnore attribute on the members that shouldn't be serialized. See the example taken from here:
Consider the following (simplified) case:
public class User {
public int Id { get; set; }
public string Name { get; set; }
[ScriptIgnore]
public bool IsComplete
{
get { return Id > 0 && !string.IsNullOrEmpty(Name); }
}
}
In this case, only the Id and the Name properties will be serialized, thus the resulting JSON object would look like this:
{ Id: 3, Name: 'Test User' }
PS. Don't forget to add a reference to "System.Web.Extensions" for this to work
Sorry I decided to write another answer since none of the other answers are copy-pasteable enough.
If you don't want to decorate properties with some attributes, or if you have no access to the class, or if you want to decide what to serialize during runtime, etc. etc. here's how you do it in Newtonsoft.Json
//short helper class to ignore some properties from serialization
public class IgnorePropertiesResolver : DefaultContractResolver
{
private readonly HashSet<string> ignoreProps;
public IgnorePropertiesResolver(IEnumerable<string> propNamesToIgnore)
{
this.ignoreProps = new HashSet<string>(propNamesToIgnore);
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (this.ignoreProps.Contains(property.PropertyName))
{
property.ShouldSerialize = _ => false;
}
return property;
}
}
Usage
JsonConvert.SerializeObject(YourObject, new JsonSerializerSettings()
{ ContractResolver = new IgnorePropertiesResolver(new[] { "Prop1", "Prop2" }) });
Note: make sure you cache the ContractResolver object if you decide to use this answer, otherwise performance may suffer.
I've published the code here in case anyone wants to add anything
https://github.com/jitbit/JsonIgnoreProps
You can use [ScriptIgnore]:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
[ScriptIgnore]
public bool IsComplete
{
get { return Id > 0 && !string.IsNullOrEmpty(Name); }
}
}
In this case the Id and then name will only be serialized
If you are not so keen on having to decorate code with Attributes as I am, esp when you cant tell at compile time what will happen here is my solution.
Using the Javascript Serializer
public static class JsonSerializerExtensions
{
public static string ToJsonString(this object target,bool ignoreNulls = true)
{
var javaScriptSerializer = new JavaScriptSerializer();
if(ignoreNulls)
{
javaScriptSerializer.RegisterConverters(new[] { new PropertyExclusionConverter(target.GetType(), true) });
}
return javaScriptSerializer.Serialize(target);
}
public static string ToJsonString(this object target, Dictionary<Type, List<string>> ignore, bool ignoreNulls = true)
{
var javaScriptSerializer = new JavaScriptSerializer();
foreach (var key in ignore.Keys)
{
javaScriptSerializer.RegisterConverters(new[] { new PropertyExclusionConverter(key, ignore[key], ignoreNulls) });
}
return javaScriptSerializer.Serialize(target);
}
}
public class PropertyExclusionConverter : JavaScriptConverter
{
private readonly List<string> propertiesToIgnore;
private readonly Type type;
private readonly bool ignoreNulls;
public PropertyExclusionConverter(Type type, List<string> propertiesToIgnore, bool ignoreNulls)
{
this.ignoreNulls = ignoreNulls;
this.type = type;
this.propertiesToIgnore = propertiesToIgnore ?? new List<string>();
}
public PropertyExclusionConverter(Type type, bool ignoreNulls)
: this(type, null, ignoreNulls){}
public override IEnumerable<Type> SupportedTypes
{
get { return new ReadOnlyCollection<Type>(new List<Type>(new[] { this.type })); }
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
var result = new Dictionary<string, object>();
if (obj == null)
{
return result;
}
var properties = obj.GetType().GetProperties();
foreach (var propertyInfo in properties)
{
if (!this.propertiesToIgnore.Contains(propertyInfo.Name))
{
if(this.ignoreNulls && propertyInfo.GetValue(obj, null) == null)
{
continue;
}
result.Add(propertyInfo.Name, propertyInfo.GetValue(obj, null));
}
}
return result;
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
throw new NotImplementedException(); //Converter is currently only used for ignoring properties on serialization
}
}
If you are using System.Text.Json then you can use [JsonIgnore].
FQ: System.Text.Json.Serialization.JsonIgnoreAttribute
Official Microsoft Docs: JsonIgnoreAttribute
As stated here:
The library is built-in as part of the .NET Core 3.0 shared framework.
For other target frameworks, install the System.Text.Json NuGet
package. The package supports:
.NET Standard 2.0 and later versions
.NET Framework 4.6.1 and later versions
.NET Core 2.0, 2.1, and 2.2
For C# 9's records it's [property: JsonIgnore]
using System.Text.Json.Serialization;
public record R(
string Text2
[property: JsonIgnore] string Text2)
For the classic style it's still just [JsonIgnore].
using System.Text.Json.Serialization;
public record R
{
public string Text {get; init; }
[JsonIgnore]
public string Text2 { get; init; }
}
You can also use the [NonSerialized] attribute
[Serializable]
public struct MySerializableStruct
{
[NonSerialized]
public string hiddenField;
public string normalField;
}
From the MS docs:
Indicates that a field of a serializable class should not be serialized. This class cannot be inherited.
If you're using Unity for example (this isn't only for Unity) then this works with UnityEngine.JsonUtility
using UnityEngine;
MySerializableStruct mss = new MySerializableStruct
{
hiddenField = "foo",
normalField = "bar"
};
Debug.Log(JsonUtility.ToJson(mss)); // result: {"normalField":"bar"}
Add System.Text.Json Version for dotnet core
For compile time, add [JsonIgnore] as suggested in the above answer.
For run time, JsonConverter needs to be added into the options.
First, create a JsonConverter for the type you want to exclude, for example ICollection<LabMethod> below
public class LabMethodConverter : JsonConverter<ICollection<LabMethod>>
{
public override ICollection<LabMethod> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
//deserialize JSON into a ICollection<LabMethod>
return null;
}
public override void Write(Utf8JsonWriter writer, ICollection<LabMethod> value, JsonSerializerOptions options)
{
//serialize a ICollection<LabMethod> object
writer.WriteNullValue();
}
}
Then add to options when you serialize Json
var options = new JsonSerializerOptions();
options.Converters.Add(new LabMethodConverter());
var new_obj = Json(new { rows = slice, total = count }, options);