I have the below Model class,
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
public class FormField
{
[Required]
[JsonPropertyName("STD_USERTYPEID")]
public string UserTypeId { get; set; }
[Required]
[JsonPropertyName("STD_OFFICETYPEID")]
public string OfficeTypeId { get; set; }
}
I have a few scenarios where STD_OFFICETYPEID may come as LegacyOFFICETYPEID or OfficeID. Is there a way in which I can dynamically generate JsonPropertyName?
I am using System.Text.Json NuGet package.
Note that this example is simplified. In my production code there could be 20-25 concrete properties. And all of these properties could map to 5-10 different JsonPropertyNames each.
I'll tell you how I would do this: source generators.
First I would inject a new attribute, JsonPropertyNames(params string[] alternativeNames), and I'd decorate my class with it instead, giving it the full list of possible field names.
Then I'd have the source generator generate a second class matching properties with my original class, including properties for each of the alternative names provided using JsonPropertyNames. This is the class you'd be reading the Json into, and all your properties would get read in one of the properties.
Then the generator would add all the necessary AutoMapper mapping code to copy from my generated type to the original type, as well as a helper class that reads into the generated type and invokes AutoMapper to return the class for you.
So from the caller side, you'd just need to call one function to get your type, ignoring all the details behind the scenes.
if you are ready to swith to Newtonsoft.Json you can try this code
var json="{\"STD_USERTYPEID\":\"userId\",\"LegacyOFFICETYPEID\":\"officeId\"}";
FormField formField = DeserializeObj<FormField>(json);
public class FormField
{
[JsonProperty("STD_USERTYPEID")]
public string UserTypeId { get; set; }
[JsonPropertyNames(new string[] {"STD_OFFICETYPEID", "LegacyOFFICETYPEID" })]
public string OfficeTypeId { get; set; }
}
public T DeserializeObj<T>(string json) where T:new()
{
var jsonObj = JObject.FromObject(new T());
var attrs = GetAttrs<T>();
var jsonParsed = JObject.Parse(json);
foreach (var prop in jsonParsed.Properties())
{
var propName=prop.Name;
var attr=attrs.Where(a=>a.AttributeNames.Contains(propName)).FirstOrDefault();
if(attr!=null) jsonObj[attr.PropertyName]=prop.Value;
else jsonObj[propName]=prop.Value;
}
return jsonObj.ToObject<T>();
}
public static List<PropertyAttributes> GetAttrs<T>() where T: new()
{
var source= new T();
var attrs = new List<PropertyAttributes>();
foreach (PropertyInfo prop in source.GetType().GetProperties())
{
var attribute = prop.GetCustomAttribute<JsonPropertyNamesAttribute>();
if (attribute != null)
{
attrs.Add(new PropertyAttributes { PropertyName = prop.Name, AttributeNames = attribute.Names });
}
}
if (attrs.Count > 0) return attrs;
return null;
}
public class PropertyAttributes
{
public string PropertyName { get; set; }
public string[] AttributeNames { get; set; }
}
[AttributeUsage(AttributeTargets.All)]
public class JsonPropertyNamesAttribute : Attribute
{
private string[] names;
public JsonPropertyNamesAttribute(string[] names)
{
this.names = names;
}
public virtual string[] Names
{
get { return names; }
}
}
I have this POCO class with properties that use a custom attribute:
Application status flags POCO class
public class ApplicationStatusFlags
{
public int ApplicationId { get; set; }
[SectionFlag("APPLICANTPERSONALDETAILS")]
public bool PersonalDetailsStatus { get; set; }
[SectionFlag("APPLICANTECREGISTRATION")]
public bool EcRegistrationStatus { get; set; }
[SectionFlag("APPLICANTCV")]
public bool CvUpload { get; set; }
[SectionFlag("APPLICANTSTATEMENT")]
public bool IceAttributeStatement { get; set; }
[SectionFlag("APPLICANTCPD")]
public bool CpdUpload { get; set; }
[SectionFlag("APPLICANTORGCHART")]
public bool OrgChartUpload { get; set; }
[SectionFlag("APPLICANTSPONSORDETAILS")]
public bool SponsorDetails { get; set; }
}
Section flag attribute class
[AttributeUsage(AttributeTargets.All)]
public class SectionFlagAttribute : Attribute
{
/// <summary>
/// This constructor takes name of attribute
/// </summary>
/// <param name="name"></param>
public SectionFlagAttribute(string name)
{
Name = name;
}
public virtual string Name { get; }
}
I'm trying to get the value of one of these properties by using a string with the section flag name.
So if var foo = "APPLICANTSPONSORDETAILS" I would get the boolean value of SponsorDetails.
Sample code
updateAppStatusFlag.ApplicationId = applicationId;
var applicationStatuses =
await _applicationService
.UpdateApplicationStatusFlagsAsync<ApplicationStatusFlags>(updateAppStatusFlag);
var foo = "APPLICANTSPONSORDETAILS";
var type = applicationStatuses.GetType();
var test = type.
GetCustomAttributes(false)
.OfType<SectionFlagAttribute>()
.SingleOrDefault()
?.Name == foo;
Any ideas how to do this? I know I can use reflection but I've had problems getting it to work.
Thanks
In your example, you're getting the customattributes of the class instead of the properties.
Here is an example:
private object GetValueBySectionFlag(object obj, string flagName)
{
// get the type:
var objType = obj.GetType();
// iterate the properties
var prop = (from property in objType.GetProperties()
// iterate it's attributes
from attrib in property.GetCustomAttributes(typeof(SectionFlagAttribute), false).Cast<SectionFlagAttribute>()
// filter on the name
where attrib.Name == flagName
// select the propertyInfo
select property).FirstOrDefault();
// use the propertyinfo to get the instance->property value
return prop?.GetValue(obj);
}
Note: this will return only the first property which contains the SectionFlagAttribute with the right name. You could modify the method to return multiple values. (like a collection of propertyname/value)
Usage:
// a test instance.
var obj = new ApplicationStatusFlags { IceAttributeStatement = true };
// get the value by SectionFlag name
var iceAttributeStatement = GetValueBySectionFlag(obj, "APPLICANTSTATEMENT");
If the returned value is null then the flag is not found or the property's value is null.
Building an OpenGraph .NET Parser but stuck in property binding. I simple fetch the HTML Document and parse it using HtmlAgilityPack. After that I want to check each Node for the specific OpenGraph Key:
Custom Attribute
public class OpenGraphAttribute : Attribute
{
public string Name { get; set; }
public OpenGraphAttribute(string name)
{
Name = name;
}
}
Container Class
public class OGVideoContainer
{
[OpenGraphAttribute("og:video:url")]
public string DefaultUrl { get; set; }
[OpenGraphAttribute("og:video:secure_url")]
public string SecureUrl { get; set; }
[OpenGraphAttribute("og:video:type")]
public string Type { get; set; }
[OpenGraphAttribute("og:video:width")]
public string Width { get; set; }
[OpenGraphAttribute("og:video:height")]
public string Height { get; set; }
[OpenGraphAttribute("og:video:url")]
public string Url { get; set; }
}
Parser
public OGVideoContainer ParseVideo(HtmlDocument doc)
{
var result = new OGVideoContainer();
var parseableAttr = typeof(OGVideoContainer).GetProperties();
foreach (var prop in parseableAttr)
{
var ca = prop.GetCustomAttributes(true).ElementAtOrDefault(0) as OpenGraphAttribute;
if (doc.DocumentNode.SelectSingleNode(String.Format("/html/head/meta[#property='{0}']", ca.Name)) != null)
{
// i am stuck here how can i access the result.propery value?
}
}
return result;
}
But stuck at the result.parameter binding. I have to assign result.DefaultUrl with the corresponding custom attribute name value. How can this be done?
Thanks for any help.
Use prop.GetValue(result) to get the property value.
Thanks. The Setter can be reflected as follows:
var targets = result.GetType().GetProperties();
targets.FirstOrDefault(m => m.Name == prop.Name).SetValue(result, "Nice String here");
I'am using ValueInjector(3.x) over AutoMapper but I have some questions.
First, I don't understand the difference between UnflatLoopInjection and FlatLoopInjection.
Also i want to set values in complex types.
Class Product {
public string Id { get; set; }
public string Name { get; set; }
public Category Category { get; set; }
}
Class ProductDTO {
public string Name { get; set; }
public Category Category { get; set; }
}
var product = repository.Get(id);
product.InjectFrom(dto);
The problem is my product.Category already have some properties with values and using InjectFrom the value injector replace the product.Category to dto.Category replacing the entire category even replacing to null.
Thanks
flattening is when you go from
Foo1.Foo2.Foo1.Name to Foo1Foo2Foo1Name
unflattening the other way around
I understand that you want to avoid injecting when the source property is Null
for this you can create an injections like this:
public class AvoidNullProps : LoopInjection
{
protected override void SetValue(object source, object target, PropertyInfo sp, PropertyInfo tp)
{
var val = sp.GetValue(source);
if(val != null)
tp.SetValue(target, val);
}
}
and use it
res.InjectFrom<AvoidNullProps>(src);
you could also use the Mapper:
Mapper.AddMap<ProductDTO, Product>(dto =>
{
var res = new Product();
res.Id = dto.Id;
res.Name = dto.Name;
if(dto.Category != null && dto.Category.Id != null)
res.Category = Mapper.Map<Category>(dto.Category);
return res;
});
var product = Mapper.Map<Product>(dto);
I have two C# classes that have many of the same properties (by name and type). I want to be able to copy all non-null values from an instance of Defect into an instance of DefectViewModel. I was hoping to do it with reflection, using GetType().GetProperties(). I tried the following:
var defect = new Defect();
var defectViewModel = new DefectViewModel();
PropertyInfo[] defectProperties = defect.GetType().GetProperties();
IEnumerable<string> viewModelPropertyNames =
defectViewModel.GetType().GetProperties().Select(property => property.Name);
IEnumerable<PropertyInfo> propertiesToCopy =
defectProperties.Where(defectProperty =>
viewModelPropertyNames.Contains(defectProperty.Name)
);
foreach (PropertyInfo defectProperty in propertiesToCopy)
{
var defectValue = defectProperty.GetValue(defect, null) as string;
if (null == defectValue)
{
continue;
}
// "System.Reflection.TargetException: Object does not match target type":
defectProperty.SetValue(viewModel, defectValue, null);
}
What would be the best way to do this? Should I maintain separate lists of Defect properties and DefectViewModel properties so that I can do viewModelProperty.SetValue(viewModel, defectValue, null)?
Edit: thanks to both Jordão's and Dave's answers, I chose AutoMapper. DefectViewModel is in a WPF application, so I added the following App constructor:
public App()
{
Mapper.CreateMap<Defect, DefectViewModel>()
.ForMember("PropertyOnlyInViewModel", options => options.Ignore())
.ForMember("AnotherPropertyOnlyInViewModel", options => options.Ignore())
.ForAllMembers(memberConfigExpr =>
memberConfigExpr.Condition(resContext =>
resContext.SourceType.Equals(typeof(string)) &&
!resContext.IsSourceValueNull
)
);
}
Then, instead of all that PropertyInfo business, I just have the following line:
var defect = new Defect();
var defectViewModel = new DefectViewModel();
Mapper.Map<Defect, DefectViewModel>(defect, defectViewModel);
Take a look at AutoMapper.
There are frameworks for this, the one I know of is Automapper:
http://automapper.codeplex.com/
http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/01/22/automapper-the-object-object-mapper.aspx
Replace your erroneous line with this:
PropertyInfo targetProperty = defectViewModel.GetType().GetProperty(defectProperty.Name);
targetProperty.SetValue(viewModel, defectValue, null);
Your posted code is attempting to set a Defect-tied property on a DefectViewModel object.
In terms of organizing the code, if you don't want an external library like AutoMapper, you can use a mixin-like scheme to separate the code out like this:
class Program {
static void Main(string[] args) {
var d = new Defect() { Category = "bug", Status = "open" };
var m = new DefectViewModel();
m.CopyPropertiesFrom(d);
Console.WriteLine("{0}, {1}", m.Category, m.Status);
}
}
// compositions
class Defect : MPropertyGettable {
public string Category { get; set; }
public string Status { get; set; }
// ...
}
class DefectViewModel : MPropertySettable {
public string Category { get; set; }
public string Status { get; set; }
// ...
}
// quasi-mixins
public interface MPropertyEnumerable { }
public static class PropertyEnumerable {
public static IEnumerable<string> GetProperties(this MPropertyEnumerable self) {
return self.GetType().GetProperties().Select(property => property.Name);
}
}
public interface MPropertyGettable : MPropertyEnumerable { }
public static class PropertyGettable {
public static object GetValue(this MPropertyGettable self, string name) {
return self.GetType().GetProperty(name).GetValue(self, null);
}
}
public interface MPropertySettable : MPropertyEnumerable { }
public static class PropertySettable {
public static void SetValue<T>(this MPropertySettable self, string name, T value) {
self.GetType().GetProperty(name).SetValue(self, value, null);
}
public static void CopyPropertiesFrom(this MPropertySettable self, MPropertyGettable other) {
self.GetProperties().Intersect(other.GetProperties()).ToList().ForEach(
property => self.SetValue(property, other.GetValue(property)));
}
}
This way, all the code to achieve the property-copying is separate from the classes that use it. You just need to reference the mixins in their interface list.
Note that this is not as robust or flexible as AutoMapper, because you might want to copy properties with different names or just some sub-set of the properties. Or it might downright fail if the properties don't provide the necessary getters or setters or their types differ. But, it still might be enough for your purposes.
This is cheap and easy. It makes use of System.Web.Script.Serialization and some extention methods for ease of use:
public static class JSONExts
{
public static string ToJSON(this object o)
{
var oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
return oSerializer.Serialize(o);
}
public static List<T> FromJSONToListOf<T>(this string jsonString)
{
var oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
return oSerializer.Deserialize<List<T>>(jsonString);
}
public static T FromJSONTo<T>(this string jsonString)
{
var oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
return oSerializer.Deserialize<T>(jsonString);
}
public static T1 ConvertViaJSON<T1>(this object o)
{
return o.ToJSON().FromJSONTo<T1>();
}
}
Here's some similiar but different classes:
public class Member
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsCitizen { get; set; }
public DateTime? Birthday { get; set; }
public string PetName { get; set; }
public int PetAge { get; set; }
public bool IsUgly { get; set; }
}
public class MemberV2
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsCitizen { get; set; }
public DateTime? Birthday { get; set; }
public string ChildName { get; set; }
public int ChildAge { get; set; }
public bool IsCute { get; set; }
}
And here's the methods in action:
var memberClass1Obj = new Member {
Name = "Steve Smith",
Age = 25,
IsCitizen = true,
Birthday = DateTime.Now.AddYears(-30),
PetName = "Rosco",
PetAge = 4,
IsUgly = true,
};
string br = "<br /><br />";
Response.Write(memberClass1Obj.ToJSON() + br); // just to show the JSON
var memberClass2Obj = memberClass1Obj.ConvertViaJSON<MemberV2>();
Response.Write(memberClass2Obj.ToJSON()); // valid fields are filled
For one thing I would not place that code (somewhere) external but in the constructor of the ViewModel:
class DefectViewModel
{
public DefectViewModel(Defect source) { ... }
}
And if this is the only class (or one of a few) I would not automate it further but write out the property assignments. Automating it looks nice but there may be more exceptions and special cases than you expect.
Any chance you could have both classes implement an interface that defines the shared properties?