Nullable datetime to datetime converter automapper - datetime

I have a situation where my DTOs require DateTime properties but my POCOs use nullable datetimes. To avoid having to create ForMember mappings for every property with this condition I created an ITypeConverter<DateTime?, DateTime>. The problem I ran into is when both DTO and POCO have nullable DateTimes this converter is called. The DestinationType is DateTime even though the property is a nullable datetime. Any idea how I would make this converter run only for actual nullable datetimes?
public class FooDTO
{
public DateTime? FooDate { get; set; }
}
public class FooPoco
{
public DateTime? FooDate { get; set; }
}
class Program
{
static void Main(string[] args)
{
Mapper.CreateMap<FooDTO, FooPoco>();
Mapper.CreateMap<DateTime?, DateTime>()
.ConvertUsing<NullableDateTimeConverter>();
var poco = new FooPoco();
Mapper.Map(new FooDTO() { FooDate = null }, poco);
if (poco.FooDate.HasValue)
Console.WriteLine(
"This should be null : {0}",
poco.FooDate.Value.ToString()); //Value is always set
else
Console.WriteLine("Mapping worked");
}
}
public class NullableDateTimeConverter : ITypeConverter<DateTime?, DateTime>
{
// Since both are nullable date times and this handles converting
// nullable to datetime I would not expect this to be called.
public DateTime Convert(ResolutionContext context)
{
var sourceDate = context.SourceValue as DateTime?;
if (sourceDate.HasValue)
return sourceDate.Value;
else
return default(DateTime);
}
}
I found this post AutoMapper TypeConverter mapping nullable type to not-nullable type but it was little help.

Without looking I suspect its calling your TypeCoverter because its the best match for the types being converted.
If you create another TypeConverter with the correct types it should work fine. Eg:
public class DateTimeConverter : ITypeConverter<DateTime?, DateTime>
{
public DateTime Convert(ResolutionContext context)
{
var sourceDate = context.SourceValue as DateTime?;
if (sourceDate.HasValue)
return sourceDate.Value;
else
return default(DateTime);
}
}
public class NullableDateTimeConverter : ITypeConverter<DateTime?, DateTime?>
{
public DateTime? Convert(ResolutionContext context)
{
var sourceDate = context.SourceValue as DateTime?;
if (sourceDate.HasValue)
return sourceDate.Value;
else
return default(DateTime?);
}
}
Note that if you wish these can be further simplified to
public class DateTimeConverter : TypeConverter<DateTime?, DateTime>
{
protected override DateTime ConvertCore(DateTime? source)
{
if (source.HasValue)
return source.Value;
else
return default(DateTime);
}
}
public class NullableDateTimeConverter : TypeConverter<DateTime?, DateTime?>
{
protected override DateTime? ConvertCore(DateTime? source)
{
return source;
}
}
Then just initialise both converters:
Mapper.CreateMap<DateTime?, DateTime>().ConvertUsing<DateTimeConverter>();
Mapper.CreateMap<DateTime?, DateTime?>().ConvertUsing<NullableDateTimeConverter>();
Mapper.AssertConfigurationIsValid();

Related

How to apply Command Design pattern with Dependency Injection using Generic Class?

i want to apply command Design pattern with dependency Injection in the following situation: i have three types of reports in my system (SubscriptionReport, SalesReport, SpechialistReport) so i created one interface IReportService
public interface IReportService<T> where T: class
{
public Task<GenericResponse<List<T>>> GetReport(string searchKey, DateTime from, DateTime to);
}
and to apply OCP i have implemented the GetReport function tree times for (SubscriptionReport, SalesReport, SpechialistReport)
public class SpechialistReportService : IReportService<SpechialistReportDTO>
{
public Task<GenericResponse<List<SpechialistReportDTO>>> Report(string searchKey, DateTime from, DateTime to)
{
throw new NotImplementedException(); // to be implemented later
}
}
public class SubscriptionReportService : IReportService<SubscriptionReportDTO>
{
public Task<GenericResponse<List<SubscriptionReportDTO>>> Report(string searchKey, DateTime from, DateTime to)
{
throw new NotImplementedException(); // to be implemented later
}
}
public class SalesReportService : IReportService<SalesReportDTO>
{
public Task<GenericResponse<List<SalesReportDTO>>> Report(string searchKey, DateTime from, DateTime to)
{
throw new NotImplementedException(); // to be implemented later
}
}
after that i have added the dependency
services.AddScoped(typeof(IReportService<SpechialistReportDTO>), typeof(SpechialistReportService));
services.AddScoped(typeof(IReportService<SubscriptionReportDTO>), typeof(SubscriptionReportService));
services.AddScoped(typeof(IReportService<SalesReportDTO>), typeof(SalesReportService));
the problem is in calling the dependency in the controller constructor
private readonly IEnumerable<IReportService> _reportService; // Should be IReportService<dont know what class should i specify here>
public ReportController(IReportService<T> reportService)
{
this._reportService = reportService;
}
Any help would be appreciated thanks in advance,
Okay i solved this problem by removing the Generic and adding marker interface to the DTOs classes
public interface ReportRoot
{
}
public class SubscriptionReportDTO : ReportRoot
{
// Some data here
}
public class SalesReportDTO: ReportRoot
{
// Some data here
}
In ReportService Interface
public interface IReportService
{
public Task<GenericResponse<List<ReportRoot>>> Report();
}
public class SubscriptionReportService : IReportService {
public async Task<GenericResponse<List<ReportRoot>>> Report()
{
List<ReportRoot> subscriptionReportDTO = new List<ReportRoot>();
SubscriptionReportDTO test = new SubscriptionReportDTO();
test.SalesTax = "1000";
subscriptionReportDTO.Add(test);
return new GenericResponse<List<ReportRoot>>("1", subscriptionReportDTO.Count, "Success", subscriptionReportDTO);
}
}
public class SalesReportService : IReportService {
public async Task<GenericResponse<List<ReportRoot>>> Report()
{
List<ReportRoot> salesReportDTO = new List<ReportRoot>();
SalesReportDTO test = new SalesReportDTO ();
test.SalesTax = "1000";
salesReportDTO .Add(test);
return new GenericResponse<List<ReportRoot>>("1", salesReportDTO.Count, "Success", salesReportDTO );
}
}
In controller
private readonly IEnumerable<IReportService> _reportService;
public ReportController(IEnumerable<IReportService> reportService)
{
this._reportService = reportService;
}

JSON.NET won't deserialize class correctly

I am trying to make a class (that contains NodaTime's ZonedDateTime properties) to be deserialized correctly (using JSON.NET) but it doesn't seem to work.
I am referencing and use NodaTime.Serialization.JsonNet as well.
The serialization goes well and the resulting JSON is correct, but the deserialization generates wrong ZonedDateTime values.
Before using NodaTime.Serialization.JsonNet I had written my own custom serializer for JSON.NET and I had the same problem. What I've noticed, is that the ReadJson() method of my custom JsonConverter was producing the correct deserialized ZonedDateTime value, but when the constructor of the class hosting the ZonedDateTime properties was called, the input values for the ZonedDateTime properties were wrong.
Here is the code:
class Program
{
static void Main(string[] args)
{
var obj = new ZonedTimeDetails(ZonedDateTime.FromDateTimeOffset(DateTime.Now), ZonedDateTime.FromDateTimeOffset(DateTime.Now.AddHours(1)), false);
var json = JsonConvert.SerializeObject(obj, new FullJsonSerializerSettings());
var obj2 = JsonConvert.DeserializeObject<ZonedTimeDetails>(json, new FullJsonSerializerSettings());
return;
}
}
public class FullJsonSerializerSettings : JsonSerializerSettings
{
public FullJsonSerializerSettings()
{
ContractResolver = new AcTypeContractResolver((MemberInfo memberInfo) => {
if (memberInfo is PropertyInfo pi)
{
var methodInfo = pi.GetSetMethod(true);
if (methodInfo == null)
{
return o => false;
}
}
return o => true;
});
TypeNameHandling = TypeNameHandling.All;
TypeNameAssemblyFormat = FormatterAssemblyStyle.Full;
Converters.Add(NodaConverters.CreateZonedDateTimeConverter(DateTimeZoneProviders.Serialization));
}
}
public class AcTypeContractResolver : DefaultContractResolver
{
private readonly Predicate<object> _predicate;
private readonly Func<MemberInfo, Predicate<object>> _predicateFactory;
public AcTypeContractResolver(Predicate<object> predicate)
{
_predicate = predicate;
}
public AcTypeContractResolver(Func<MemberInfo, Predicate<object>> predicateFactory)
{
_predicateFactory = predicateFactory;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.Ignored = false;
property.ShouldSerialize = _predicate ?? _predicateFactory?.Invoke(member);
property.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
return property;
}
}
public class ZonedTimeDetails
{
[JsonConstructor]
public ZonedTimeDetails(ZonedDateTime zoneStart, ZonedDateTime zoneEnd, bool isOverflow = false)
{
ZonedStart = zoneStart;
ZonedEnd = zoneEnd;
IsOverflow = isOverflow;
}
public ZonedDateTime ZonedStart { get; private set; }
public ZonedDateTime ZonedEnd { get; private set; }
public bool IsOverflow { get; private set; }
public DateTime Start => ZonedStart.ToDateTimeUnspecified();
public DateTime End => ZonedEnd.ToDateTimeUnspecified();
public double DurationMin => (ZonedEnd - ZonedStart).TotalMinutes;
}
And here is the entire project, if that helps you:
https://mega.nz/#!hFc0RAbS!teJ3Y4JHqCx1aHxUVU4kUFs30xwTTyF6QTpRB0D1Fnw
If someone has any idea what is going wrong, let me know.
From what I can tell, the problem is in the ZonedTimeDetails class, but I believe that this ought to be working. The property names of that class match the ctor argument names, so I cannot understand why I get wrong values in there during deserialization.
UPDATE:
If I make the setters for the ZonedDateTime properties public, it works, but I need that class to be immutable. And according to other answers on SO (1, 2), this constructor injection should work.
In addition to that, before I use ZonedDateTime, I was using DateTime properties in that class. And things were working without a problem with the setters private.
The problem is that the parameter names in the ZonedTimeDetails constructor do not match up with the JSON, which you are creating from serializing that same class. The property names have a d (e.g. ZonedStart) whereas the constructor property names do not (zoneStart). So when the constructor is called, empty structs are being passed into those parameters.
To fix, just change your constructor parameter names to match the property names:
[JsonConstructor]
public ZonedTimeDetails(ZonedDateTime zonedStart, ZonedDateTime zonedEnd, bool isOverflow = false)
{
ZonedStart = zonedStart;
ZonedEnd = zonedEnd;
IsOverflow = isOverflow;
}

ASP MVC Data Annotation - How to verify that two model properties are different using CompareAttribute

I regularly use the CompareAttribute data annotation in my models to validate that two fields are equal. For example, most of us use it to compare a password and a confirm password field.
It may seems trivial but I wonder how to use such annotation to compare that two fields are differents. For instance, I would like to verify that the password is different from the username.
For more complex validations, I know I have to use custom validators, but I was just wondering if there was something built-in for that.
Thank you.
You have two choices here, create your own ValidationAttribute inheriting from CompareAttribute or inheriting from ValidationAttribute.
1) Custom ValidationAttribute inherit from CompareAttribute
public class NotEqualAttribute : CompareAttribute
{
public string BasePropertyName { get; private set; }
private const string DefaultErrorMessage = "'{0}' and '{1}' must not be the same.";
public MyCustomCompareAttribut(string otherProperty)
: base(otherProperty)
{
BasePropertyName = otherProperty;
}
public override string FormatErrorMessage(string name)
{
return string.Format(DefaultErrorMessage, name, BasePropertyName);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var result = base.IsValid(value, validationContext);
if (result == null)
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
return null;
}
}
2)Custom ValidationAttribute inherit from ValidationAttribute
public class NotEqualAttribute : ValidationAttribute
{
private const string DefaultErrorMessage = "'{0}' and '{1}' must not be the same.";
public string BasePropertyName { get; private set; }
public NotEqualAttribute(string basePropertyName)
: base(DefaultErrorMessage)
{
BasePropertyName = basePropertyName;
}
public override string FormatErrorMessage(string name)
{
return string.Format(DefaultErrorMessage, name, BasePropertyName);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(BasePropertyName);
if (property == null)
{
return new ValidationResult(
string.Format(
CultureInfo.CurrentCulture, "{0} is invalid property", BasePropertyName
)
);
}
var otherValue = property.GetValue(validationContext.ObjectInstance, null);
if (object.Equals(value, otherValue))
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
return null;
}
}
Then you can use either one like:
public class YourModelClass
{
public string PropertyA{ get; set; }
[NotEqual("PropertyA")]
public string PropertyB{ get; set; }
}

xml serialization error on bool types

I am trying to find out how to solve the problem for serializing a type of bool from a camel case string.
I have the following xml
<Root>
<BoolElement>
False
</BoolElement>
</Root>
and the following class
[XmlRoot("Root")]
public class RootObj{
[XmlElement("BoolElement")]
public bool BoolElement{get;set;}
}
this will produce an error.
If I use the same class and rename the "False" to "false" it will work. The problem is that I can't edit the xml.
Does anyone know how can I solve this?
You could use a backing field to aid for the deserialization of this invalid XML (I say invalid because according to the xsd:boolean schema False is an invalid value):
[XmlRoot("Root")]
public class RootObj
{
[XmlElement("BoolElement")]
public string BackingBoolElement
{
set
{
BoolElement = bool.Parse(value.ToLower());
}
get
{
return BoolElement.ToString();
}
}
[XmlIgnore]
public bool BoolElement { get; set; }
}
False is not a valid value for an xsd:boolean (but as you note false and 0 are) - if you cannot change the source data, then you could have a separate property purely for XML serialisation:
[XmlRoot("Root")]
public class RootObj{
[XmlElement("BoolElement")]
public string BoolElementForSerialization
{
get
{
return (this.BoolElement ? "True" : "False");
}
set
{
this.BoolElement = (string.Compare(value, "false", StringComparison.OrdinalIgnoreCase) != 0);
}
}
[XmlIgnore]
public bool BoolElement{get;set;}
}
I created a new Boolean type that can deserialize from any string. It may not be perfect but it suited my needs at the time.
For the class you want to use simply change the data type from bool to SerializableBoolean:
[XmlRoot("Root")]
public class RootObj{
[XmlElement("BoolElement")]
public SerializableBoolean BoolElement{get;set;}
}
You can then use the BoolElement property like any normal bool data type:
RootObj myObj = new RootObj();
if (myObj.BoolElement) { ... }
Here is the code for the SerializableBoolean class, note this code only handles deserializing, serializing to xml wasn't required for my purposes and so not implemented.
[System.Diagnostics.DebuggerDisplay("{Value}")]
public struct SerializableBoolean: System.Xml.Serialization.IXmlSerializable
{
private bool Value { get; set; }
public override bool Equals(object obj)
{
if (obj is string stringBoolean)
{
bool.TryParse(stringBoolean, out bool boolean);
return Value == boolean;
}
else if (obj is bool boolean)
{
return Value == boolean;
}
else if (obj is SerializableBoolean serializableBoolean)
{
return Value == serializableBoolean.Value;
}
else
{
return Value == Convert.ToBoolean(obj);
}
}
public override int GetHashCode()
{
return -1937169414 + Value.GetHashCode();
}
public XmlSchema GetSchema()
{
throw new NotImplementedException();
}
public void ReadXml(XmlReader reader)
{
Value = Convert.ToBoolean(reader.ReadElementContentAsString());
}
public void WriteXml(XmlWriter writer)
{
throw new NotImplementedException();
}
public static bool operator ==(SerializableBoolean obj1, bool obj2)
{
return obj1.Value.Equals(obj2);
}
public static bool operator !=(SerializableBoolean obj1, bool obj2)
{
return !obj1.Value.Equals(obj2);
}
public static implicit operator SerializableBoolean(string value)
{
return new SerializableBoolean() { Value = Convert.ToBoolean(value) };
}
public static implicit operator SerializableBoolean(bool value)
{
return new SerializableBoolean() { Value = value };
}
public static implicit operator bool(SerializableBoolean b)
{
return b.Value;
}
}

JSON.Net - Change $type field to another name?

When using Json.Net, I understand how to get the $type property into the rendered json, but is there a way to change that field name? I need to use "__type" instead of "$type".
http://json.codeplex.com/workitem/22429
"I would rather keep $type hard coded and consistent."
Consistent with what I wonder?
http://json.codeplex.com/workitem/21989
I would rather not - I think this is too specific to me and I don't
want to go overboard with settings. At some point I will probably
implement this - http://json.codeplex.com/workitem/21856 - allowing
people to read/write there own meta properties in the JSON and you
could reimplement type name handling with a new property name. The
other option is just to modify the source code for yourself to have
that property name.
And more recently, Issue #36: Customizable $type property name feature:
I'd rather not
This is my solution...
json.Replace("\"$type\": \"", "\"type\": \"");
Looks like this is hardcoded as public const string TypePropertyName = "$type"; in Newtonsoft.Json.Serialization.JsonTypeReflector which is internal static class unfortunately.
I needed this myself, and the only thing I can think of is having custom modified version of json.net itself. Which is of course is a major pita.
when serializing, there is a nice way to override the property name:
public class CustomJsonWriter : JsonTextWriter
{
public CustomJsonWriter(TextWriter writer) : base(writer)
{
}
public override void WritePropertyName(string name, bool escape)
{
if (name == "$type") name = "__type";
base.WritePropertyName(name, escape);
}
}
var serializer = new JsonSerializer();
var writer = new StreamWriter(stream) { AutoFlush = true };
serializer.Serialize(new CustomJsonWriter(writer), objectToSerialize);
I haven't tried deserialization yet, but in worst case I could use:
json.Replace("\"__type": \"", "\"type\": \"$type\");
I had to do this for my UI REST API as Angular.js disregards fields names starting with a dollar sign ($).
So here's a solution that renames $type to __type for the whole Web API and works both for serialization and deserialization.
In order to be able to use a custom JsonWriter and a custom JsonReader (as proposed in the other answers to this question), we have to inherit the JsonMediaTypeFormatter and override the corresponding methods:
internal class CustomJsonNetFormatter : JsonMediaTypeFormatter
{
public override JsonReader CreateJsonReader(Type type, Stream readStream, Encoding effectiveEncoding)
{
return new CustomJsonReader(readStream, effectiveEncoding);
}
public override JsonWriter CreateJsonWriter(Type type, Stream writeStream, Encoding effectiveEncoding)
{
return new CustomJsonWriter(writeStream, effectiveEncoding);
}
private class CustomJsonWriter : JsonTextWriter
{
public CustomJsonWriter(Stream writeStream, Encoding effectiveEncoding)
: base(new StreamWriter(writeStream, effectiveEncoding))
{
}
public override void WritePropertyName(string name, bool escape)
{
if (name == "$type") name = "__type";
base.WritePropertyName(name, escape);
}
}
private class CustomJsonReader : JsonTextReader
{
public CustomJsonReader(Stream readStream, Encoding effectiveEncoding)
: base(new StreamReader(readStream, effectiveEncoding))
{
}
public override bool Read()
{
var hasToken = base.Read();
if (hasToken && TokenType == JsonToken.PropertyName && Value != null && Value.Equals("__type"))
{
SetToken(JsonToken.PropertyName, "$type");
}
return hasToken;
}
}
}
Of course you need to register the custom formatter in your WebApiConfig. So we replace the default Json.NET formatter with our custom one:
config.Formatters.Remove(config.Formatters.JsonFormatter);
config.Formatters.Add(new CustomJsonNetFormatter());
Done.
We had a need for this so I created a custom JsonReader. We're are using rest in our MS web services with complex data models and needed to replace the "__type" property with "$type."
class MSJsonReader : JsonTextReader
{
public MSJsonTextReader(TextReader reader) : base(reader) { }
public override bool Read()
{
var hasToken = base.Read();
if (hasToken && base.TokenType == JsonToken.PropertyName && base.Value != null && base.Value.Equals("__type"))
base.SetToken(JsonToken.PropertyName, "$type");
return hasToken;
}
}
Here is how we use it.
using(JsonReader jr = new MSJsonTextReader(sr))
{
JsonSerializer s = new JsonSerializer();
s.DateFormatHandling = DateFormatHandling.MicrosoftDateFormat;
s.NullValueHandling = NullValueHandling.Ignore;
s.TypeNameHandling = TypeNameHandling.Auto; // Important!
s.Binder = new MSRestToJsonDotNetSerializationBinder("Server.DataModelsNamespace", "Client.GeneratedModelsNamespace");
T deserialized = s.Deserialize<T>(jr);
return deserialized;
}
Here is our MSRestToJsonDotNetSerializationBinder that completes the compatibility between MS rest and Json.Net.
class MSRestToJsonDotNetSerializationBinder : System.Runtime.Serialization.SerializationBinder
{
public string ServiceNamespace { get; set; }
public string LocalNamespace { get; set; }
public MSRestToJsonDotNetSerializationBinder(string serviceNamespace, string localNamespace)
{
if (serviceNamespace.EndsWith("."))
serviceNamespace = serviceNamespace.Substring(0, -1);
if(localNamespace.EndsWith("."))
localNamespace = localNamespace.Substring(0, -1);
ServiceNamespace = serviceNamespace;
LocalNamespace = localNamespace;
}
public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
assemblyName = null;
typeName = string.Format("{0}:#{1}", serializedType.Name, ServiceNamespace); // MS format
}
public override Type BindToType(string assemblyName, string typeName)
{
string jsonDotNetType = string.Format("{0}.{1}", LocalNamespace, typeName.Substring(0, typeName.IndexOf(":#")));
return Type.GetType(jsonDotNetType);
}
}
You could also do it this way:
[JsonConverter(typeof(JsonSubtypes), "ClassName")]
public class Annimal
{
public virtual string ClassName { get; }
public string Color { get; set; }
}
You will need the JsonSubtypes
converter that is not part of Newtonsoft.Json project.
There is another option that allows to serialize custom type property name in Json.NET. The idea is do not write default $type property, but introduce type name as property of class itself.
Suppose we have a Location class:
public class Location
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}
First, we need to introduce type property name and modify the class as demonstrated below:
public class Location
{
[JsonProperty("__type")]
public string EntityTypeName
{
get
{
var typeName = string.Format("{0}, {1}", GetType().FullName, GetType().Namespace);
return typeName;
}
}
public double Latitude { get; set; }
public double Longitude { get; set; }
}
Then, set JsonSerializerSettings.TypeNameHandling to TypeNameHandling.None in order the deserializer to skip the rendering of default $type attribute.
That's it.
Example
var point = new Location() { Latitude = 51.5033630, Longitude = -0.1276250 };
var jsonLocation = JsonConvert.SerializeObject(point, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.None, //do not write type property(!)
});
Console.WriteLine(jsonLocation);
Result
{"__type":"Namespace.Location, Namespace","Latitude":51.503363,"Longitude":-0.127625}
Using a custom converter should get the job done.
public CustomConverter : JsonConverter
{
public override bool CanWrite => true;
public override bool CanRead => true;
public override object ReadJson(JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
=> throw new NotImplementedException();
public override void WriteJson(JsonWriter writer,
object value,
JsonSerializer serializer)
{
var jOjbect = (JObject)JToken.FromObject(value);
jOjbect.Add(new JProperty("type", value.GetType().Name));
jOjbect.WriteTo(writer);
}
}

Resources