I have this object
[DataContract]
public class FilterList<T> : List<T>
{
[DataMember]
public int Total { get; set; }
}
In my controller:
public ActionResult<FilterList<MyPOCO>> GetFilteredResult(string filter)
{
var l = new FilterList<MyPOCO>();
l.Total = 123456;
// Continue to add many MyPOCO objects into the list
return l;
}
I can get back the MyPOCO list at the client side, but the l.Total is NOT serialize. May I know what I had done wrongly?
Here is a workaround , you could try to use [JsonObject] attribute . But the items will not be serialized, because a JSON container can have properties, or items -- but not both. If you want both, you will need to add a synthetic list property to hold the items.
[JsonObject] will also cause base class properties such as Capacity to be serialized, which you likely do not want. To suppress base class properties, use MemberSerialization.OptIn. Thus your final class should look something like:
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class FilterList<T> : List<T>
{
[JsonProperty]
public int Total { get; set; }
[JsonProperty]
List<T> Items
{
get
{
return this.ToList();
}
set
{
if (value != null)
this.AddRange(value);
}
}
}
Result:
Related
With a simple class/interface like this
public interface IThing
{
string Name { get; set; }
}
public class Thing : IThing
{
public int Id { get; set; }
public string Name { get; set; }
}
How can I get the JSON string with only the "Name" property (only the properties of the underlying interface) ?
Actually, when i make that :
var serialized = JsonConvert.SerializeObject((IThing)theObjToSerialize, Formatting.Indented);
Console.WriteLine(serialized);
I get the full object as JSON (Id + Name);
The method I use,
public class InterfaceContractResolver : DefaultContractResolver
{
private readonly Type _InterfaceType;
public InterfaceContractResolver (Type InterfaceType)
{
_InterfaceType = InterfaceType;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
//IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
IList<JsonProperty> properties = base.CreateProperties(_InterfaceType, memberSerialization);
return properties;
}
}
// To serialize do this:
var settings = new JsonSerializerSettings() {
ContractResolver = new InterfaceContractResolver (typeof(IThing))
};
string json = JsonConvert.SerializeObject(theObjToSerialize, settings);
Improved version with nested interfaces + support for xsd.exe objects
Yet another variation here. The code came from http://www.tomdupont.net/2015/09/how-to-only-serialize-interface.html with the following improvements over other answers here
Handles hierarchy, so if you have an Interface2[] within an Interface1 then it will get serialized.
I was trying to serialize a WCF proxy object and the resultant JSON came up as {}. Turned out all properties were set to Ignore=true so I had to add a loop to set them all to not being ignored.
public class InterfaceContractResolver : DefaultContractResolver
{
private readonly Type[] _interfaceTypes;
private readonly ConcurrentDictionary<Type, Type> _typeToSerializeMap;
public InterfaceContractResolver(params Type[] interfaceTypes)
{
_interfaceTypes = interfaceTypes;
_typeToSerializeMap = new ConcurrentDictionary<Type, Type>();
}
protected override IList<JsonProperty> CreateProperties(
Type type,
MemberSerialization memberSerialization)
{
var typeToSerialize = _typeToSerializeMap.GetOrAdd(
type,
t => _interfaceTypes.FirstOrDefault(
it => it.IsAssignableFrom(t)) ?? t);
var props = base.CreateProperties(typeToSerialize, memberSerialization);
// mark all props as not ignored
foreach (var prop in props)
{
prop.Ignored = false;
}
return props;
}
}
Inspired by #user3161686, here's a small modification to InterfaceContractResolver:
public class InterfaceContractResolver<TInterface> : DefaultContractResolver where TInterface : class
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> properties = base.CreateProperties(typeof(TInterface), memberSerialization);
return properties;
}
}
You can use conditional serialization. Take a look at this link. Basicly, you need to implement the IContractResolver interface, overload the ShouldSerialize method and pass your resolver to the constructor of the Json Serializer.
An alternative to [JsonIgnore] are the [DataContract] and [DataMember] attributes. If you class is tagged with [DataContract] the serializer will only process properties tagged with the [DataMember] attribute (JsonIgnore is an "opt-out" model while DataContract is "op-in").
[DataContract]
public class Thing : IThing
{
[DataMember]
public int Id { get; set; }
public string Name { get; set; }
}
The limitation of both approaches is that they must be implemented in the class, you cannot add them to the interface definition.
You can add the [JsonIgnore] annotation to ignore an attribute.
I'd like to share what we ended up doing when confronted with this task. Given the OP's interface and class...
public interface IThing
{
string Name { get; set; }
}
public class Thing : IThing
{
public int Id { get; set; }
public string Name { get; set; }
}
...we created a class that is the direct implementation of the interface...
public class DirectThing : IThing
{
public string Name { get; set; }
}
Then simply serialized our Thing instance, deserialized it as a DirectThing, then Serialized it as a DirectThing:
var thing = new Thing();
JsonConvert.SerializeObject(
JsonConvert.DeserializeObject<DirectThing>(JsonConvert.SerializeObject(thing)));
This approach can work with a long interface inheritance chain...you just need to make a direct class (DirectThing in this example) at the level of interest. No need to worry about reflection or attributes.
From a maintenance perspective, the DirectThing class is easy to maintain if you add members to IThing because the compiler will give errors if you haven't also put them in DirectThing. However, if you remove a member X from IThing and put it in Thing instead, then you'll have to remember to remove it from DirectThing or else X would be in the end result.
From a performance perspective there are three (de)serialization operations happening here instead of one, so depending on your situation you might like to evaluate the performance difference of reflector/attribute-based solutions versus this solution. In my case I was just doing this on a small scale, so I wasn't concerned about potential losses of some micro/milliseconds.
Hope that helps someone!
in addition to the answer given by #monrow you can use the default [DataContract] and [DataMember]
have a look at this
http://james.newtonking.com/archive/2009/10/23/efficient-json-with-json-net-reducing-serialized-json-size.aspx
Finally I got when it will not work...
If you want to have inside another complex object it will not be properly serialized.
So I have made version which will extract only data stored in specific assembly and for types which have the same base interface.
So it is made as .Net Core JsonContractResolver.
In addition to data extraction it solves:
a) camelCase conversion before sending data to client
b) uses top most interface from allowed scope (by assembly)
c) fixes order of fields: field from most base class will be listed first and nested object will meet this rule as well.
public class OutputJsonResolver : DefaultContractResolver
{
#region Static Members
private static readonly object syncTargets = new object();
private static readonly Dictionary<Type, IList<JsonProperty>> Targets = new Dictionary<Type, IList<JsonProperty>>();
private static readonly Assembly CommonAssembly = typeof(ICommon).Assembly;
#endregion
#region Override Members
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
if (type.Assembly != OutputJsonResolver.CommonAssembly)
return base.CreateProperties(type, memberSerialization);
IList<JsonProperty> properties;
if (OutputJsonResolver.Targets.TryGetValue(type, out properties) == false)
{
lock (OutputJsonResolver.syncTargets)
{
if (OutputJsonResolver.Targets.ContainsKey(type) == false)
{
properties = this.CreateCustomProperties(type, memberSerialization);
OutputJsonResolver.Targets[type] = properties;
}
}
}
return properties;
}
protected override string ResolvePropertyName(string propertyName)
{
return propertyName.ToCase(Casing.Camel);
}
#endregion
#region Assistants
private IList<JsonProperty> CreateCustomProperties(Type type, MemberSerialization memberSerialization)
{
// Hierarchy
IReadOnlyList<Type> types = this.GetTypes(type);
// Head
Type head = types.OrderByDescending(item => item.GetInterfaces().Length).FirstOrDefault();
// Sources
IList<JsonProperty> sources = base.CreateProperties(head, memberSerialization);
// Targets
IList<JsonProperty> targets = new List<JsonProperty>(sources.Count);
// Repository
IReadOnlyDistribution<Type, JsonProperty> repository = sources.ToDistribution(item => item.DeclaringType);
foreach (Type current in types.Reverse())
{
IReadOnlyPage<JsonProperty> page;
if (repository.TryGetValue(current, out page) == true)
targets.AddRange(page);
}
return targets;
}
private IReadOnlyList<Type> GetTypes(Type type)
{
List<Type> types = new List<Type>();
if (type.IsInterface == true)
types.Add(type);
types.AddRange(type.GetInterfaces());
return types;
}
#endregion
}
I have this API
public ActionResult AddDocument([FromBody]AddDocumentRequestModel documentRequestModel)
{
AddDocumentStatus documentState = _documentService.AddDocument(documentRequestModel, DocumentType.OutgoingPosShipment);
if (documentState.IsSuccess)
return Ok();
return BadRequest();
}
And this is my request model
public class AddDocumentRequestModel
{
public AddDocumentRequestModel(int partnerId, List<ProductRequestModel> products)
{
PartnerId = partnerId;
Products = products;
}
[Range(1, int.MaxValue, ErrorMessage = "Value for {0} must be between {1} and {2}.")]
public int PartnerId { get; private set; }
[Required, MustHaveOneElement(ErrorMessage = "At least one product is required")]
public List<ProductRequestModel> Products { get; private set; }
}
so when I'm trying to hit the API with this body
{
"partnerId": 101,
"products": [{
"productId": 100,
"unitOfMeasureId": 102,
"quantity":5
}
]
}
this is the request : System.NotSupportedException: Deserialization of reference types without parameterless constructor is not supported. Type 'Alati.Commerce.Sync.Api.Controllers.AddDocumentRequestModel'
I don't need parameterless constructor,because it doesn't read the body parameters.Is there any other way for deserialization?
You can achieve your desired result. You need to switch to NewtonsoftJson serialization (from package Microsoft.AspNetCore.Mvc.NewtonsoftJson)
Call this in Startup.cs in the ConfigureServices method:
services.AddControllers().AddNewtonsoftJson();
After this, your constructor will be called by deserialization.
Extra info: I am using ASP Net Core 3.1
Later Edit: I wanted to give more info on this, as it seems that this can also be achieved by using System.Text.Json, although custom implementation is necessary. The answer from jawa states that Deserializing to immutable classes and structs can be achieved with System.Text.Json, by creating a custom converter (inherit from JsonConverter) and registering it to the converters collection (JsonSerializerOptions.Converters) like so:
public class ImmutablePointConverter : JsonConverter<ImmutablePoint>
{
...
}
and then...
var serializeOptions = new JsonSerializerOptions();
serializeOptions.Converters.Add(new ImmutablePointConverter());
serializeOptions.WriteIndented = true;
Just in case someone have the same issue I had, I was using abstract class, once removed the abstract key word, it all worked just fine.
Just Add [JsonConstructor] before your constructor
like this
public class Person
{
public string Name { get; set; }
public int LuckyNumber { get; private set; }
[JsonConstructor]
public Person(int luckyNumber)
{
LuckyNumber = luckyNumber;
}
public Person() { }
}
There are still some limitations using System.Text.Json - have a look here: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#table-of-differences-between-newtonsoftjson-and-systemtextjson
Deserialization without parameterless constructor using a parameterized constructor is not supported yet (but it's on their plan). You can implement your custom JsonConverter (like in this example: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#deserialize-to-immutable-classes-and-structs) or - like Adrian Nasul above suggested: use Newtonsoft.Json and then you can use the [JsonConstructor] attribute
In my case I had set a class as internal and when I made it public it worked. The error message was really of little help with this specific circumstance.
Old (actual class name changed to ClassName in the example
internal class Rootobject
{
[JsonConstructor]
public Rootobject(ClassName className)
{
ClassName = className?? throw new ArgumentNullException(nameof(className));
}
public ClassName ClassName { get; set; }
}
New:
public class Rootobject
{
[JsonConstructor]
public Rootobject(ClassName className)
{
ClassName = branding ?? throw new ArgumentNullException(nameof(className));
}
public ClassName ClassName { get; set; }
}
In my case error, caused was inside InnerException. There is my class had a field with a custom class type that did not have a parameterless constructor. I've added a parameterless constructor to the inner class and the problem has gone away.
I'm trying to understand why this binding doesn't work.
The binding didn't work until I changed the type from struct to class.
Is this by design or am I missing something?
I'm using asp.net core 2.2 MVC
View Models
Not working
public class SettingsUpdateModel
{
public DeviceSettingsStruct DeviceSettings { get; set; }
}
Working
public class SettingsUpdateModel
{
public DeviceSettingsClass DeviceSettings { get; set; }
}
public class DeviceSettingsClass
{
public bool OutOfScheduleAlert { get; set; }
// other fields removed for brevity
}
public struct DeviceSettingsStruct
{
public bool OutOfScheduleAlert { get; set; }
// other fields removed for brevity
}
Controller
[HttpPost]
public IActionResult Update(SettingsUpdateModel newSettings)
{
// newSettings.DeviceSettings.OutOfScheduleAlert always false on struct but correct on class
return Index(null);
}
View
<input class="form-check-input" type="checkbox" id="out_of_schedule_checkbox" asp-for="DeviceSettings.OutOfScheduleAlert">
Expected: DeviceSettings.OutOfScheduleAlert to bind to a struct the same as class
Actual: only the class parameter was binded
It is by design in complex type model bindings. A struct type is a value type that is typically used to encapsulate small groups of related variables, such as the coordinates of a rectangle or the characteristics of an item in an inventory.
In ComplexTypeModelBinder.cs , the CanUpdateReadOnlyProperty method will mark the properties of value-type model as readonly due to value types have copy-by-value semantics, which prevents us from updating
internal static bool CanUpdatePropertyInternal(ModelMetadata propertyMetadata)
{
return !propertyMetadata.IsReadOnly || CanUpdateReadOnlyProperty(propertyMetadata.ModelType);
}
private static bool CanUpdateReadOnlyProperty(Type propertyType)
{
// Value types have copy-by-value semantics, which prevents us from updating
// properties that are marked readonly.
if (propertyType.GetTypeInfo().IsValueType)
{
return false;
}
// Arrays are strange beasts since their contents are mutable but their sizes aren't.
// Therefore we shouldn't even try to update these. Further reading:
// http://blogs.msdn.com/ericlippert/archive/2008/09/22/arrays-considered-somewhat-harmful.aspx
if (propertyType.IsArray)
{
return false;
}
// Special-case known immutable reference types
if (propertyType == typeof(string))
{
return false;
}
return true;
}
Reference here for more details .
BTY ,if you want to bind struct type model , you could try to send the json data by using ajax request from the view.
We have a problem concerning Entity Framework objects and sending them through WCF.
We have a database, and Entity Framework created classes from that database, a 'Wallet' class in this particular situation.
We try to transfer a Wallet using this code:
public Wallet getWallet()
{
Wallet w = new Wallet();
w.name = "myname";
w.walletID = 123;
return w;
}
We need to transfer that Wallet class, but it won't work, we always encounter the same exception:
"An error occurred while receiving the HTTP response to localhost:8860/ComplementaryCoins.svc. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details."
We searched on the internet, and there is a possibility that the error is due to the need of serialization of Entity Framework-objects.
We have absolutely no idea if this could be the case, and if this is the case, how to solve it.
Our DataContract looks like this (very simple):
[DataContract]
public partial class Wallet
{
[DataMember]
public int getwalletID { get { return walletID; } }
[DataMember]
public string getname { get { return name; } }
}
Does anyone ever encountered this problem?
EDIT: Our Entity Framework created class looks like this:
namespace ComplementaryCoins
{
using System;
using System.Collections.Generic;
public partial class Wallet
{
public Wallet()
{
this.Transaction = new HashSet<Transaction>();
this.Transaction1 = new HashSet<Transaction>();
this.User_Wallet = new HashSet<User_Wallet>();
this.Wallet_Item = new HashSet<Wallet_Item>();
}
public int walletID { get; set; }
public string name { get; set; }
public virtual ICollection<Transaction> Transaction { get; set; }
public virtual ICollection<Transaction> Transaction1 { get; set; }
public virtual ICollection<User_Wallet> User_Wallet { get; set; }
public virtual ICollection<Wallet_Item> Wallet_Item { get; set; }
}
}
Thanks for helping us.
I had the same problem some time ago and the solution for this was:
The entity framework was returning a serialized class instead of normal class.
eg. Wallet_asfawfklnaewfklawlfkawlfjlwfejlkef instead of Wallet
To solve that you can add this code:
base.Configuration.ProxyCreationEnabled = false;
in your Context file.
Since the context file is auto generated you can add it in the Context.tt
In the Context.tt file it can be added around lines 55-65:
<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
{
public <#=code.Escape(container)#>()
: base("name=<#=container.Name#>")
{
base.Configuration.ProxyCreationEnabled = false;
<#
if (!loader.IsLazyLoadingEnabled(container))
{
#>
this.Configuration.LazyLoadingEnabled = false;
<#
Try specifying a setter for the properties, something like this :
[DataContract]
public partial class Wallet
{
[DataMember]
public int getwalletID { get { return walletID; } set { } }
[DataMember]
public string getname { get { return name; } set { } }
}
If it still doesn't work, you may consider creating an intermediate POCO class for this purpose, and use mapper library like AutoMapper or ValueInjecter to transfer the data from the EF objects.
The POCO class should have same properties as your EF class :
[DataContract]
public class WalletDTO
{
[DataMember]
public int walletID { get; set; }
[DataMember]
public string name { get; set; }
}
And modify your method to return this class instead :
public WalletDTO getWallet()
{
Wallet w = new Wallet(); // or get it from db using EF
var dto = new WalletDTO();
//assuming we are using ValueInjecter, this code below will transfer all matched properties from w to dto
dto.InjectFrom(w);
return dto;
}
Are you trying to recieve a IEnumerable<Wallets>? If - yes, please modify your server class that returns the IEnumerable by adding .ToArray() method
I have spent some time writing code for my application assuming that the serialisation bit would be the easiest part of it. Pretty much both sides (client and server) are done and all I need to do is passing a class AccountInfo from the service to the client... The problem is that AccountInfo inherits List and therefore [DataContract] attribute is not valid. I tried using the [CollectionDataContract] attribute but then the class that is received on the other side (client) contains only generic List methods without my custom implemented properties such as GroupTitle...I have worked out a solution for this problem but I don't know how to apply it.
Basically everything works when I make a property instead of inheriting a List but then I can't bind this class to LongListSelector (WP7) because it's not a collection type.
There are three classes I'm on about. AccountInfo that contains multiple instances of: AccountInfoGroup that contains multiple instances of:AccountInfoEntry (this one does not inherit list therefore there are no problems serialising it and all properties are accessible).
Could someone help me using right attributes to serialise and transfer these classes using a WCF method?
Here is the code of 2 of these collection classes:
public class AccountInfo : List<AccountInfoGroup>
{
public AccountInfo()
{
UpdateTime = DateTime.UtcNow;
EntryID = Guid.NewGuid();
}
public bool HasItems
{
get
{
return (Count != 0);
}
private set
{
}
}
public Guid EntryID
{
get;
set;
}
public decimal GetTotalCredit()
{
decimal credit = 0;
foreach (AccountInfoGroup acg in this.Where(item => item.Class == AccountInfoEntry.EntryType.Credit))
{
acg.Where(item => item.ItemClass == AccountInfoEntry.EntryType.Credit).ToList().ForEach(entry =>
{ credit += entry.Remaining; }
);
}
return credit;
}
public bool UsedForCreditComparison = false;
public DateTime UpdateTime { get; private set; }
}
public class AccountInfoGroup : List<AccountInfoEntry>
{
public AccountInfoEntry.EntryType Class
{
get;
private set;
}
public string Title
{
get
{
return AccountInfoEntry.ClassToString(Class);
}
}
public AccountInfoGroup(AccountInfoEntry.EntryType groupClass)
{
this.#Class = groupClass;
}
public bool HasItems
{
get
{
return (Count != 0);
}
private set
{
}
}
}
Thank you for any suggestions... :)
The sample you had is quite painful for WCF in serialization.
What I suggest is you to revised and have a common models for your WCF messages (That means it only contains properties with getter and setter, serialization attributes).
If you have a problem in LongListSelector binding in WP7, you might want to convert the message to the actual type the WP7 object supports to use in binding.