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);
Related
I am using System.Text.Json for deserialization.
I want to use the constructor in SerialNo(string serialNo) to build my object.
public class SerialNo
{
[JsonConstructor]
public SerialNo(string serialNo)
{
if (serialNo == null) throw new ArgumentNullException(nameof(serialNo));
if (string.IsNullOrWhiteSpace(serialNo)) throw new Exception("My exception text");
Value = serialNo.Trim('0');
}
public string Value { get; set; }
}
public class Item
{
public SerialNo SerialNo { get; set; }
public string AffiliationOrgCode { get; set; }
}
public class Root
{
public List<Item> Item { get; set; }
}
public class DeserializationTestsWithSystemTextJson
{
private const string JsonString = #"
{
""item"": [
{
""serialNo"": ""000000000002200878"",
""affiliationOrgCode"": ""OrgCode1""
},
{
""serialNo"": ""000000000002201675"",
""affiliationOrgCode"": ""OrgCode1""
}
]
}
";
[Fact]
public void Simple_Deserialization_With_SystemTextJson()
{
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web);
var deserializedClass = JsonSerializer.Deserialize<Root>(JsonString, options);
Assert.NotNull(deserializedClass);
Assert.Equal("2201675", deserializedClass.Item[1].SerialNo.Value);
}
}
Fails with:
The JSON value could not be converted to JsonNetDeserialization.Test.WithSystemText.SerialNo. Path: $.item[0].serialNo | LineNumber: 3 | BytePositionInLine: 44.
Any ideas?
Since you are converting a string to a SerialNo, you need a custom converter. For example:
public class SerialNoConverter : JsonConverter<SerialNo>
{
public override SerialNo? Read(ref Utf8JsonReader reader, Type typeToConvert,
JsonSerializerOptions options)
{
var serialNo = reader.GetString();
return new SerialNo(serialNo);
}
public override void Write(Utf8JsonWriter writer, SerialNo value,
JsonSerializerOptions options)
{
// Left for OP if required
throw new NotImplementedException();
}
}
And to use it you can either add an attribute to your class:
public class Item
{
[JsonConverter(typeof(SerialNoConverter))]
public SerialNo SerialNo { get; set; }
public string AffiliationOrgCode { get; set; }
}
Or add the converter to your serialiser options:
options.Converters.Add(new SerialNoConverter());
Here is a running example.
You are able to use a constructor to deserialize JSON using System.Text.Json, contrary to what the marked answer says. Even Microsoft documents it: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/immutability?pivots=dotnet-7-0
There are a some drawbacks though:
All class/struct fields/properties must match the JSON property name exactly
All json properties must not be nested in a deeper JSON object
To deserialize JSON with a constructor, you just need to use [JsonConstructor]. Here's an example of deserializing this way:
JSON:
{
"Fp": 2.199,
"Pi": 3.14159,
"Str": "Hello World"
}
The deserialized class:
public class Dto
{
public double Fp { get; set; }
public double Pi { get; set; }
public string Str { get; set; }
[JsonConstructor]
public Dto(double fp, double pi, string str)
{
Fp = fp;
Pi = pi;
Str = str;
}
public override string ToString()
{
return $"{nameof(Fp)}: {Fp}, {nameof(Pi)}: {Pi}, {nameof(Str)}: {Str}";
}
}
Now to deserialize, you just include this expression: var dto = JsonSerializer.Deserialize<Dto>(Json); In the event you are trying to deserialize are custom type, you can include the [JsonConverter(...)] attribute over the custom type properties/fields in your deserialized class if the custom types do not already have a JsonConverter attribute for their class. Because the class properties use the exact name as in the Json, all other types are still deserialized properly and the custom type property will use the attributed custom converter. Here is an example of using a custom converter:
public class Dto
{
public double Fp { get; set; }
public double Pi { get; set; }
[JsonConverter(typeof(CustomStringConverter))] // This can be removed if the CustomString class already has a JsonConverter attribute
public CustomString Str { get; set; }
[JsonConstructor]
public Dto(double fp, double pi, CustomString str)
{
this.Fp = fp;
Pi = pi;
Str = str;
}
public override string ToString()
{
return $"{nameof(Fp)}: {Fp}, {nameof(Pi)}: {Pi}, {nameof(Str)}: {Str}";
}
}
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.
Is there a way to change name of Data property during serialization, so I can reuse this class in my WEB Api.
For an example, if i am returning paged list of users, Data property should be serialized as "users", if i'm returning list of items, should be called "items", etc.
Is something like this possible:
public class PagedData
{
[JsonProperty(PropertyName = "Set from constructor")]??
public IEnumerable<T> Data { get; private set; }
public int Count { get; private set; }
public int CurrentPage { get; private set; }
public int Offset { get; private set; }
public int RowsPerPage { get; private set; }
public int? PreviousPage { get; private set; }
public int? NextPage { get; private set; }
}
EDIT:
I would like to have a control over this functionality, such as passing name to be used if possible. If my class is called UserDTO, I still want serialized property to be called Users, not UserDTOs.
Example
var usersPagedData = new PagedData("Users", params...);
You can do this with a custom ContractResolver. The resolver can look for a custom attribute which will signal that you want the name of the JSON property to be based on the class of the items in the enumerable. If the item class has another attribute on it specifying its plural name, that name will then be used for the enumerable property, otherwise the item class name itself will be pluralized and used as the enumerable property name. Below is the code you would need.
First let's define some custom attributes:
public class JsonPropertyNameBasedOnItemClassAttribute : Attribute
{
}
public class JsonPluralNameAttribute : Attribute
{
public string PluralName { get; set; }
public JsonPluralNameAttribute(string pluralName)
{
PluralName = pluralName;
}
}
And then the resolver:
public class CustomResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty prop = base.CreateProperty(member, memberSerialization);
if (prop.PropertyType.IsGenericType && member.GetCustomAttribute<JsonPropertyNameBasedOnItemClassAttribute>() != null)
{
Type itemType = prop.PropertyType.GetGenericArguments().First();
JsonPluralNameAttribute att = itemType.GetCustomAttribute<JsonPluralNameAttribute>();
prop.PropertyName = att != null ? att.PluralName : Pluralize(itemType.Name);
}
return prop;
}
protected string Pluralize(string name)
{
if (name.EndsWith("y") && !name.EndsWith("ay") && !name.EndsWith("ey") && !name.EndsWith("oy") && !name.EndsWith("uy"))
return name.Substring(0, name.Length - 1) + "ies";
if (name.EndsWith("s"))
return name + "es";
return name + "s";
}
}
Now you can decorate the variably-named property in your PagedData<T> class with the [JsonPropertyNameBasedOnItemClass] attribute:
public class PagedData<T>
{
[JsonPropertyNameBasedOnItemClass]
public IEnumerable<T> Data { get; private set; }
...
}
And decorate your DTO classes with the [JsonPluralName] attribute:
[JsonPluralName("Users")]
public class UserDTO
{
...
}
[JsonPluralName("Items")]
public class ItemDTO
{
...
}
Finally, to serialize, create an instance of JsonSerializerSettings, set the ContractResolver property, and pass the settings to JsonConvert.SerializeObject like so:
JsonSerializerSettings settings = new JsonSerializerSettings
{
ContractResolver = new CustomResolver()
};
string json = JsonConvert.SerializeObject(pagedData, settings);
Fiddle: https://dotnetfiddle.net/GqKBnx
If you're using Web API (looks like you are), then you can install the custom resolver into the pipeline via the Register method of the WebApiConfig class (in the App_Start folder).
JsonSerializerSettings settings = config.Formatters.JsonFormatter.SerializerSettings;
settings.ContractResolver = new CustomResolver();
Another Approach
Another possible approach uses a custom JsonConverter to handle the serialization of the PagedData class specifically instead using the more general "resolver + attributes" approach presented above. The converter approach requires that there be another property on your PagedData class which specifies the JSON name to use for the enumerable Data property. You could either pass this name in the PagedData constructor or set it separately, as long as you do it before serialization time. The converter will look for that name and use it when writing out JSON for the enumerable property.
Here is the code for the converter:
public class PagedDataConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(PagedData<>);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Type type = value.GetType();
var bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
string dataPropertyName = (string)type.GetProperty("DataPropertyName", bindingFlags).GetValue(value);
if (string.IsNullOrEmpty(dataPropertyName))
{
dataPropertyName = "Data";
}
JObject jo = new JObject();
jo.Add(dataPropertyName, JArray.FromObject(type.GetProperty("Data").GetValue(value)));
foreach (PropertyInfo prop in type.GetProperties().Where(p => !p.Name.StartsWith("Data")))
{
jo.Add(prop.Name, new JValue(prop.GetValue(value)));
}
jo.WriteTo(writer);
}
public override bool CanRead
{
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use this converter, first add a string property called DataPropertyName to your PagedData class (it can be private if you like), then add a [JsonConverter] attribute to the class to tie it to the converter:
[JsonConverter(typeof(PagedDataConverter))]
public class PagedData<T>
{
private string DataPropertyName { get; set; }
public IEnumerable<T> Data { get; private set; }
...
}
And that's it. As long as you've set the DataPropertyName property, it will be picked up by the converter on serialization.
Fiddle: https://dotnetfiddle.net/8E8fEE
UPD Sep 2020: #RyanHarlich pointed that proposed solution doesn't work out of the box. I found that Newtonsoft.Json doesn't initialize getter-only properties in newer versions, but I'm pretty sure it did ATM I wrote this answer in 2016 (no proofs, sorry :).
A quick-n-dirty solution is to add public setters to all properties ( example in dotnetfiddle ). I encourage you to find a better solution that keeps read-only interface for data objects. I haven't used .Net for 3 years, so cannot give you that solution myself, sorry :/
Another option with no need to play with json formatters or use string replacements - only inheritance and overriding (still not very nice solution, imo):
public class MyUser { }
public class MyItem { }
// you cannot use it out of the box, because it's abstract,
// i.e. only for what's intended [=implemented].
public abstract class PaginatedData<T>
{
// abstract, so you don't forget to override it in ancestors
public abstract IEnumerable<T> Data { get; }
public int Count { get; }
public int CurrentPage { get; }
public int Offset { get; }
public int RowsPerPage { get; }
public int? PreviousPage { get; }
public int? NextPage { get; }
}
// you specify class explicitly
// name is clear,.. still not clearer than PaginatedData<MyUser> though
public sealed class PaginatedUsers : PaginatedData<MyUser>
{
// explicit mapping - more agile than implicit name convension
[JsonProperty("Users")]
public override IEnumerable<MyUser> Data { get; }
}
public sealed class PaginatedItems : PaginatedData<MyItem>
{
[JsonProperty("Items")]
public override IEnumerable<MyItem> Data { get; }
}
Here is a solution that doesn't require any change in the way you use the Json serializer. In fact, it should also work with other serializers. It uses the cool DynamicObject class.
The usage is just like you wanted:
var usersPagedData = new PagedData<User>("Users");
....
public class PagedData<T> : DynamicObject
{
private string _name;
public PagedData(string name)
{
if (name == null)
throw new ArgumentNullException(nameof(name));
_name = name;
}
public IEnumerable<T> Data { get; private set; }
public int Count { get; private set; }
public int CurrentPage { get; private set; }
public int Offset { get; private set; }
public int RowsPerPage { get; private set; }
public int? PreviousPage { get; private set; }
public int? NextPage { get; private set; }
public override IEnumerable<string> GetDynamicMemberNames()
{
yield return _name;
foreach (var prop in GetType().GetProperties().Where(p => p.CanRead && p.GetIndexParameters().Length == 0 && p.Name != nameof(Data)))
{
yield return prop.Name;
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (binder.Name == _name)
{
result = Data;
return true;
}
return base.TryGetMember(binder, out result);
}
}
The following is another solution tested in .NET Standard 2.
public class PagedResult<T> where T : class
{
[JsonPropertyNameBasedOnItemClassAttribute]
public List<T> Results { get; set; }
[JsonProperty("count")]
public long Count { get; set; }
[JsonProperty("total_count")]
public long TotalCount { get; set; }
[JsonProperty("current_page")]
public long CurrentPage { get; set; }
[JsonProperty("per_page")]
public long PerPage { get; set; }
[JsonProperty("pages")]
public long Pages { get; set; }
}
I am using Humanizer for pluralization.
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (member.GetCustomAttribute<JsonPropertyNameBasedOnItemClassAttribute>() != null)
{
Type[] arguments = property.DeclaringType.GenericTypeArguments;
if(arguments.Length > 0)
{
string name = arguments[0].Name.ToString();
property.PropertyName = name.ToLower().Pluralize();
}
return property;
}
return base.CreateProperty(member, memberSerialization);
}
There's a package called SerializationInterceptor. Here's the GitHub link: https://github.com/Dorin-Mocan/SerializationInterceptor/wiki. You can also install the package using Nuget Package Manager.
The example from below uses Syste.Text.Json for serialization. You can use any other serializer(except Newtonsoft.Json). For more info on why Newtonsoft.Json not allowed, please refer to GitHub documentation.
You can create an interceptor
public class JsonPropertyNameInterceptorAttribute : InterceptorAttribute
{
public JsonPropertyNameInterceptorAttribute(string interceptorId)
: base(interceptorId, typeof(JsonPropertyNameAttribute))
{
}
protected override void Intercept(in AttributeParams originalAttributeParams, object context)
{
string theNameYouWant;
switch (InterceptorId)
{
case "some id":
theNameYouWant = (string)context;
break;
default:
return;
}
originalAttributeParams.ConstructorArgs.First().ArgValue = theNameYouWant;
}
}
And put the interceptor on the Data prop
public class PagedData<T>
{
[JsonPropertyNameInterceptor("some id")]
[JsonPropertyName("during serialization this value will be replaced with the one passed in context")]
public IEnumerable<T> Data { get; private set; }
public int Count { get; private set; }
public int CurrentPage { get; private set; }
public int Offset { get; private set; }
public int RowsPerPage { get; private set; }
public int? PreviousPage { get; private set; }
public int? NextPage { get; private set; }
}
And then you can serialize the object like this
var serializedObj = InterceptSerialization(
obj,
objType,
(o, t) =>
{
return JsonSerializer.Serialize(o, t, new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.Preserve });
},
context: "the name you want");
Hope this will be of use to you.
have a look here:
How to rename JSON key
Its not done during serialization but with a string operation.
Not very nice (in my eyes) but at least a possibility.
Cheers Thomas
I have an object that looks something like this (obviously simplified)
public class Person {
public string Name { get; set; }
public int Age { get; set; }
public string ETag { get { return ... } }
}
What I would like is for ETag to be a hash of the json serialization of the object omitting the ETag property (to prevent a recursive loop). However, I cannot just use a [JsonIgnore] attribute since at other times I want to be able to json serialize the entire thing.
So what I want is something like this
public string ETag { get {
return Hash(JsonConvert.Serialize(this, p => p.Ignore(x=>x.ETag) ));
}}
which is unfortunately not an API that exists. How would I achieve something similar?
You can use a custom IContractResolver to programmatically ignore properties on an object. So I think the approach I would take is to create a simple resolver that can specifically ignore a single property on a single type (obviously you could extend this if needed), then make a helper method that can serialize using that resolver. Use the helper method from within your ETag property and you're good to go.
Here's the code for the resolver:
class IgnorePropertyResolver : DefaultContractResolver
{
Type targetType;
string targetPropertyName;
public IgnorePropertyResolver(Type targetType, string propertyName)
{
this.targetType = targetType;
this.targetPropertyName = propertyName;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> props = base.CreateProperties(type, memberSerialization);
if (targetType == type)
{
props = props.Where(p => p.PropertyName != targetPropertyName).ToList();
}
return props;
}
}
Here's the helper method (I also threw a Hash helper method in there since you had not defined it in your question):
static class JsonHelper
{
public static string Serialize(object target, string propertyToIgnore)
{
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.ContractResolver = new IgnorePropertyResolver(target.GetType(), propertyToIgnore);
return JsonConvert.SerializeObject(target, settings);
}
public static string Hash(string json)
{
using (var sha = new SHA1Managed())
{
return Convert.ToBase64String(sha.ComputeHash(Encoding.UTF8.GetBytes(json)));
}
}
}
And finally, here's a working demo:
class Program
{
static void Main(string[] args)
{
Person p = new Person { Name = "Joe", Age = 26 };
Console.WriteLine("Etag = " + p.ETag);
Console.WriteLine();
Console.WriteLine(JsonConvert.SerializeObject(p, Formatting.Indented));
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string ETag
{
get { return JsonHelper.Hash(JsonHelper.Serialize(this, "ETag")); }
}
}
Output:
Etag = T99YVDlrbZ66YL2u5MYjyIyO4Qk=
{
"Name": "Joe",
"Age": 26,
"ETag": "T99YVDlrbZ66YL2u5MYjyIyO4Qk="
}
Fiddle: https://dotnetfiddle.net/YgVJ4K
Its not pretty, but you can use string substitution after the JSON serialization. You might set ETag to a marker value so that after serialization you can replace/remove that element.
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.