Map/merge two objects using Automapper but ignoring default values - c#

I've already looked into a lot of the posts on SO of people having similar problems, but nothing has worked so far.
I have an two instances of an object. One is being used in another class, while the other one is being periodically (about every 5 seconds or so) updated with new information via websocket.
The first response from the websocket upon subscription is the object with all of it's properties assigned a value. The problem is that the subsequent data coming in the from websocket only brings in data that has changed, so when I go to deserialize, it sets those properties that weren't assigned a value, their default value.
I'd rather not have to do a ".ForMember" on each of the properties since this object has 52 of them.
var configuration = new MapperConfiguration(cfg =>
{
cfg.CreateMap<StreamingQuoteResponse.Content, StreamingQuoteResponse.Content>().ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null));
});
var mapper = new Mapper(configuration);
var newQuote = mapper.Map<StreamingQuoteResponse.Content>(quote);
As far as I can tell, in the above code, only the strings would be null by default. Floats would be 0; doubles, 0.0d, etc.
The object has all different types of properties (bool, double, float, char, string). Is there a way to ignore mapping of a single property if the source property is the default value? I was thinking there was a way to get the property type using AutoMapper's (Pre)Condition, and then if it's e.g. typeof(float), then we can ignore mapping if the source value is 0? I'm not sure if you can do that though.
Or maybe a subroutine/method that would resolve all of this using Reflections?

This might help someone in the future, but I ended up ditching the AutoMapper, and going with JSON.NET. I didn't know they had this 'JsonMergeSettings'. I also had to add an attribute to all the properties of the class I'm (de)serializing. For example:
public class Content
{
[JsonProperty("key", DefaultValueHandling = DefaultValueHandling.Ignore)]
public string Key { get; set; }
}
Then in the event handler that was receiving all the websocket data, I did the following:
private bool firstFlag = true;
private JObject jObject;
private void HandleStreamingWebsocketData(object sender, MessageEventArgs e)
{
if (firstFlag)
{
jObject = JObject.Parse(e.Data);
firstFlag = false;
}
jObject.Merge(JObject.Parse(e.Data), new JsonMergeSettings
{
MergeArrayHandling = MergeArrayHandling.Merge
});
}
Hopefully this helps someone trying to do the same thing. And I'm sure it can be done with AutoMapper, but this was much easier I think, and most likely a bit faster, since you're merging the JSON before it's being deserialized into a C# object, as opposed to deserializing the updated data and comparing it to the deserialized previous data, and the mapping them that way.

Related

Is a more generic solution possible?

I think what I want to do is not possible.
However, I would like to make sure that it is indeed not possible.
I use Audit.Net as a framework to add auditing to a system that has already been completed.
There are many different objects that may be sent for auditing. I have to get hold of the properties of those objects and send them to the database. In some cases, I would want the old values and the new values, and therefore I use the Target property of AuditEvent, otherwise if I just need the new values I use the CustomField property.
Is there any way to make the following more generic, so that I don't have to repeat these lines for each type of object like SimpleResult, LeaveRequest, Incident etc?
There is unfortunately no commonality between the objects being audited.
SimpleResult objOld = JsonConvert.DeserializeObject<SimpleResult>(auditEvent.Target.SerializedOld.ToString());
SimpleResult objNew = JsonConvert.DeserializeObject<SimpleResult>(auditEvent.Target.SerializedNew.ToString());
if (auditEvent.Target.Type.ToString() == "SimpleResult")
{
InsertTargetObjectFields<SimpleResult>(objOld, objNew, auditControlTableID, auditEvent);
}
Here is where I get hold of the properties and send them off to the database:
public void InsertTargetObjectFields<T>(T objOld, T objNew, int? auditControlTableID, AuditEvent auditEvent)
{
using (ESSDataContext ess_context = new ESSDataContext())
{
try
{
foreach (var property in objOld.GetType().GetProperties().Where(property => !property.GetGetMethod().GetParameters().Any()))
{
//Check for null values and get hold of oldValue and newValue
var sqlResult = ess_context.InsertAuditTable(resourceTag, dbObjectName, username, property.Name, oldValue,
newValue, auditEvent.StartDate, auditControlTableID.ToString(),
auditEvent.Environment.CallingMethodName);
}
}
}
}
I've tried using dynamic, but then I don't get the properties correctly.

Using a Dictionary<string, Type> to use the value as type of a list

I am trying to build a solution fitting with the problem of not knowing what kind of Setting type I am dealing with.
I got a Dictionary<string, Type> (which I initially wanted to make <string, class> but that didn't work)
that I want to fill with the setting code and the type of class attached to it i.e.
{ "person_customField", typeof(CustomFieldModel) }
Why I want to do this is because I have a field in my database filled with json data that should be deserialized to a List<> but I don't know what kind of setting it is until I get the object from the database. I can use the Code field to detemine what type it is (person_CustomField should use the CustomFieldModel class, but emailSetting should use EmailSettingModel to match parameters to.
Is there a way to successfully make this statement work with?
JsonConvert.DeserializeObject<List<SettingTypes[record.SettingCode]>>(record.SettingValues).ToList<ISetting>()
Or should I go a different route
Code Sample:
public static readonly Dictionary<string, Type> SettingTypes = new Dictionary<string, Type>()
{
{ "person_CustomFields", typeof(CustomFieldModel)},
};
public static TenantSettingEdit ConvertToTenantSettingEdit(this T_TenantSetting rec)
{
var test = SettingTypes[rec.TENS_Code];
TenantSettingEdit item = new TenantSettingEdit()
{
IDToken = rec.TENS_TenantSettingID.toVirtualGuid().ToString(),
Code = rec.TENS_Code,
Settings = JsonConvert.DeserializeObject<List<SettingTypes[rec.TENS_Code]>>(rec.TENS_Setting).ToList<ITenantSetting>(),
IsActive = rec.TENS_ActiveRec,
};
return item;
}
(I have done this before with PHP but I am not sure if this is even remotely possible with C#)
Why I want to do this is because I have a field in my database filled
with json data that should be deserialized to a List<> but I don't
know what kind of setting it is until I get the object from the
database.
If you're using Json.Net for JSON serialization/deserialization you can use the TypeNameHandling property to embed Type information in the resulting JSON. That JSON can the be deserialized by Json.Net without additional information. If it is necessary to map custom values to the types instead of the automatically generated ones you can use a SerializationBinder (check out this answer).
If none of those help you, you can still fall back to reflection in the way M Kloster describes.
You cannot use a variable as the type parameter in the code, no. What you need to do is to generate the type-specific method by reflection:
var genericMethod = ((Func<string, int>)Json.DeserializeObject<int>).Method.GetGenericMethodDefinition();
var boundMethod = genericMethod.MakeGenericMethod(SettingTypes[record.SettingCode]);
var result = boundMethod.Invoke(null, rec.TENS_Setting)...

How to add example values for parameters in Swagger-UI built by Nancy.Swagger package?

I have used Nancy.Swagger package and MetadataModule to build a Swagger UI for my APIs (according to this link: https://github.com/yahehe/Nancy.Swagger/wiki/Nancy.Swagger-for-Nancy-v2).
I get the UI, but the problem is that I cannot add example values for the properties of the object, which is passed as a parameter in body.
I see, for example, this output:
Here, instead of the word "string", I would like to have a real example value. But I don't know how I can add example values in this approach, and I would appreciate any help.
Snippet from the API and the parameter (object of PRequest):
Post("/", async (x, ctx) =>
{
PRequest PostRequestModel;
try
{
postRequestModel = this.Bind<PRequest>();
}
Snippet from MetaDataModule:
Describe["Post"] = desc => desc.AsSwagger(
with => with.Operation(
op => op.OperationId("Post")
.Tag("User")
.Summary("Post a new User")
.Description("This creates a new user.")
.BodyParameter(bp => bp.Description("A PRequest object").Name("PRequest").Schema<PRequest>())
I know it has been absolutely ages since you opened this, but I figured I'd share anyways.
First you need a model, like so:
public class Model
{
public string ExampleString { get; set; }
}
You need to create an instance of this model filled with whatever examples you want.
var exampleModel = new Model() { ExampleString = "foobar" }
Then you can add it to the BodyParameter like so:
.BodyParameter(para => para.Name("Example").Schema(
new Schema() { Example = exampleModel }
).Build())
I just created a public static class to hold all my example objects, then I set them like Example = Examples.Example1. I think this is the most readable approach.
A couple problems exist with this approach that I haven't found a solution to (yet). One is that the Example object does not seem to respect whatever settings you're using for Json serialization. Also, I've been unable to get this working concurrently with the Model view, but the Model view was always mostly useless in my eyes anyway. I'll update this if I ever figure any of these issues out. :)

Is it possible to send a dictionary from server to c# client?

So I'm able to send simple objects like strings no problem. I'm also able to send the same dictionary to my javascript client. However, I can't seem to get my C# client to pick it up. I'm assuming this is because in the javascript client the type being sent doesn't need to be specified, but in C# it does.
Here's the client setup
connection = new HubConnection(ServerURI);
hubProxy = connection.CreateHubProxy("ControllerHub");
hubProxy.On<Dictionary<Tuple<string, string>, RequestEntry>>("ReceiveTickerStateData", overviewDictionary => receiveTickerStateData(overviewDictionary));
connection.Start().Wait();
Here's the server code that is getting triggered to send the dictionary, but the client never receives it.
public void GetTickerStateData()
{
Clients.Caller.ReceiveTickerStateData(DealTickerState.Instance.GetRequestDict);
}
Any insight or ideas are appreciated. Thanks!
------------------------Additions
Further testing has yielded that it is the fact that I'm using a tuple (string, string) as the key for the dictionary. Apparently the JSON deserializer does not know how to handle this. Gives me the error:
Could not convert string (DEFAULT, IDCO) to dictionary key type
System.Tuple`2[System.String,System.String]. Create a TypeConverter
to convert from the string to the key type object. Path '['(DEFAULT,
IDCO)']', line 1, position 19.
I got this by manually serializing and sending the string.
I tried sending a dictionary with just a string as the key and it works fine both as a serialized string and also just as is. (ie signalR automatically serializing)
So now to decide, do I create the type converter? or do I send the data using a simpler collection structure that works.
Yes, as JSON. Use JSON.NET, the included JavaScriptSerializer won't work. You can get JSON.NET from NuGet. Convert your dictionary to JSON and send it as a string. On the client-side, just deserialize it using the built-in JSON.parse() method.
You can also create an intermediary type that stores the dictionary during transfer (I use List<KeyValuePair<T1,T2>>), it's important that you validate that there aren't any duplicate keys when deserialising this from a client.
[Serializable]
public class SerializableDictionary<T1,T2>
{
public List<KeyValuePair<T1,T2>> SerializedDictionary { get; set; }
public SerializableDictionary( Dictionary<T1,T2> dictionary )
{
if( dictionary != null && dictionary.Count > 0 )
{
SerializedDictionary = dictionary.Select( item => item ).ToList();
}
else
{
throw new ArgumentNullException( "Cannot serialize a null or empty dictionary" );
}
}
public static explicit operator SerializableDictionary<T1,T2>(Dictionary<T1,T2> dictionary)
{
return new SerializableDictionary<T1,T2>(dictionary);
}
public static explicit operator Dictionary<T1,T2>(SerializableDictionary<T1,T2> dictionary)
{
if ( dictionary.SerializedDictionary == null ) return null;
return dictionary.SerializedDictionary.ToDictionary( item => item.Key, item => item.Value );
}
}
This technique wont work for every dictionary (for example, those stored in exceptions) as they can't all be easily suplemented for this service aware dictionary.
Seems that the real answer is Yes, it is possible, but you need to write a custom JSON converter. You can find more info on that here, though the answer does seem a tad incomplete.
How to properly serialize tuple as key dictionary

Deep Reflection in .NET

I need to create the ability to drill through an objects properties like two or three deep. For instance, class A has a property reference to class B, which I need to access class C. What is the best way to do this: straight reflection, or maybe using the TypeDescriptor, or something else?
Thanks.
It's not too hard to write. I put a few classes together to deal with this so I could serialize properties of a WinForm. Take a look at this class and the related classes.
http://csharptest.net/browse/src/Library/Reflection/PropertySerializer.cs
If you know the path in a static context (ie the path is always the same) and the properties are accessible (internal or public) you can use dynamic
[Test]
public void Foo()
{
var a = new A
{
B = new B
{
C = new C
{
Name = "hello"
}
}
};
DoReflection(a);
}
private void DoReflection(dynamic value)
{
string message = value.B.C.Name;
Debug.WriteLine(message);
}
I you wanna write you own serialization code for whatever reason, you'll be using reflection.
What you do is that you write a recursive method of serlizating a type. You then apply this as you see fit to get the result.
var type = myObjectOfSomeType.GetType();
// now depending on what you want to store
// I'll save all public properties
var properties = type.GetProperties(); // get all public properties
foreach(var p in properties)
{
var value = p.GetValue(myObjectOfSomeType, null);
Writevalue(p.Name, value);
}
The implementation of WriteValue have to recognize the built in types and treat them accordingly, that's typical things like string, char, integer, double, DateTime etc.
If it encounters a sequence or collection you need to write out many values.
If it encounters a non trivial type you'll apply this recursive pattern again.
The end result is a recursive algorithm that traverses your object model and writes out values as it encounters types that I know how to serialize.
However, I do recommend looking into WCF, not for building services, but for serialization. It shipped as part of the .NET 3.0 framework with a new assembly System.Runtime.Serilization and in general is very capable when dealing with serialization and data annotations.

Categories

Resources