Replace Placeholders in Html with Model Object - c#

I have a hard time explaining what I'm exactly trying to do. There's probably a name for it, but I don't know what it is.
First, I have a model such as:
public class Customer
{
public int Id { get; set; }
public int ProductId { get; set; }
...more properties...
public virtual Product Product { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
...more properties...
}
Second, I have a string of HTML text with placeholders in {}. I'd like to have something like {Id} and have it replace the Html text with the model properties.
<div><span>Name</span><span>{Id}-{Product.Name}</span></div>
My thought was to use a NameValueCollection to get the Model properties as strings. Using reflection, I can do that for the base properties, but not for something like Product.Name.
Am I going about this the wrong way? What could I use to get a NameValueCollection that I could loop through and do a replace of the Html?
Here is the current code I have (skips virtual properties):
public virtual NameValueCollection GetNameValueCollection(Object obj)
{
Type type = obj.GetType();
PropertyInfo[] properties = type.GetProperties();
var coll = new NameValueCollection();
foreach (PropertyInfo property in properties)
{
if(!property.GetGetMethod().IsVirtual)
{
if (property.PropertyType == typeof(DateTime))
{
var date = (DateTime)property.GetValue(obj, null);
coll.Add(property.Name, date.ToLongDateString());
}
else if (property.PropertyType == typeof(DateTime?))
{
var date = (DateTime?)property.GetValue(obj, null);
if (date.HasValue)
{
coll.Add(property.Name, date.Value.ToLongDateString());
}
else
{
coll.Add(property.Name, string.Empty);
}
}
else
{
var value = property.GetValue(obj, null);
if (value != null)
{
coll.Add(property.Name, value.ToString());
}
}
}
}
return coll;
}
This should be recursive, but it seems like there should be a better way. By the way, I don't need a NameValueCollection specifically (could be Dictionary<string,string> for example). Thoughts? Is there a nuget package that already does this?

I ended up just using what I had and added a sub section for handling child objects. I didn't want to do full recursion since I only wanted the direct child objects, not all the way through the chain.

Related

How to generate JsonPropertyName dynamically?

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; }
}
}

Recursive PropertyInformation from Nested IEnumerable<Model>

I am having trouble getting the property Names of the IEnumerable properties in my models. I cant seem to get the Nested IEnumerables from the TModel classes. I have looked into some reflection examples but haven't something quite along these lines.
I am looking to just get the IEnumerable property names for each nested model and send the property name to a list. The actual value is not important.
Any help would be greatly appreciated.
// TModel = DataContent in this context.
public class GetModelBase<TModel>
{
public string Error { get; set; }
public IEnumerable<TModel> DataContent { get; set; }
}
public class DataContent
{
public int Total { get; set; }
public IEnumerable<Data> Data { get; set; }
}
public class Data
{
public int DataId{ get; set; }
IEnumerable<DataInformation> DataInformation{ get; set; }
}
public IEnumerable<GetModelBase<TModel>> ResponseAsList<TModel>()
{
// ResponseBody in this context is a string representation of json of the models above...
var toArray = new ConvertJsonArray<GetModelBase<TModel>>(ResponseBody).ReturnJsonArray();
}
// T = GetModelBase<DataContent> in this context.
public class ConvertJsonArray<T>
{
public ConvertJsonArray(string responseString)
{
_responseString = responseString;
Convert();
}
public void Convert()
{
var result = JObject.Parse(_responseString);
// This is where I am having trouble... I am unable to get the nested IEnumerable names.
Type t = typeof(T);
PropertyInfo[] propertyInformation = t.GetProperties(BindingFlags.Public|BindingFlags.Instance);
List<string> toLists = new List<string>();
foreach (PropertyInfo pi in propertyInformation)
toLists.Add(pi.Name);
// End of Property Information Issuse...
foreach (string s in toLists.ToArray())
{
if (result[s] != null)
{
if (!(result[s] is JArray)) result[s] = new JArray(result[s]);
}
}
_jsonAsArray = result.ToString();
}
public string ReturnJsonArray()
{
return _jsonAsArray;
}
private string _responseString { get; set; }
private string _jsonAsArray { get; set; }
}
The result I am looking for in the above code sample would be a list containing only the IEnumerable names as such { "DataContent", "Data", "DataInformation" }
UPDATE:
I am still having trouble looping through each model. I have a nearly working code example.
// This replaces the Type code in the Convert method...
GetProperties(typeof(T))
private void GetProperties(Type classType)
{
foreach (PropertyInfo property in classType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (property.PropertyType.IsGenericType && (property.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
{
ValuesToList.Add(property.Name);
foreach (Type nestedType in property.PropertyType.GetGenericArguments())
{
GetProperties(nestedType);
}
}
}
}
private List<string> ValuesToList { get; set; }
The results for this yields { "DataContent", "Data" } but fails to get "DataInformation". For some reason the IEnumerables are not hit while in the foreach loop. Additional help would be appreciated.
You already have the PropertyInfo, so you are almost there - all that is left is to recognize which properties are of type IEnumerable<...>, where ... can be an arbitrary type.
For this purpose, check the PropertyType property.
It is a Type instance for which you can check whether it is based upon the generic type definition IEnumerable<T> by means of the GetGenericTypeDefinition method.
That method will throw an exception for non-generic types, so you will also have to check IsGenericType:
if (pi.PropertyType.IsGenericType
&& (pi.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
{
toLists.Add(pi.Name);
}

ValueInjecter Null Values

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);

Is Class SubClass or Object

Note: I'm asking about subclasses, not derived classes.
Basically, what I need to do is check properties of an object and look for those that have a specific attribute set.
The problem I have is that a lot of the properties are from subclasses
public class ExampleAttribute : Attribute
{
public object Whatever { get; set; }
}
public class MiddleEarth
{
[Example]
public Type EntityType { get; set; }
}
public class Elf : MiddleEarth
{
[Example]
public SubClass ItsLateAndImTired { get; set; }
public IList<Arg> Args { get; set; }
//Need to check properties of this object as well
public class SubClass
{
public object SubProperty { get; set; }
[Example]
public object SubPropertyWithAttribute { get; set; }
}
public class Arg
{
[Example]
public string Something { get; set; }
}
}
Now, I'm trying to do it as follows...but for reasons noted in the comments it won't work
public List<string> IterateProperties(object _o)
{
List<string> problems = new List<string>();
foreach (PropertyInfo info in _o.GetType().GetProperties())
{
//All 3 of these will return the exact same thing
Type thisType = this.GetType();
Type oType = _o.GetType();
Type infoType = info.ReflectedType;
//IsSubClassOf only checks for derived classes,
//so it's not the method I'm looking for
if (info.ReflectedType.IsSubclassOf(this.GetType()))
{
object sub = info.GetValue(_o, null);
if (sub != null)
{
problems.AddRange(this.IterateProperties(sub));
}
}
object[] attributes = info.GetCustomAttributes(typeof(ExampleAttribute), true);
foreach (object o in attributes)
{
if (info.GetValue(_o, null) == null)
{
problems.Add(String.Format("Attribute {0} in class {1} cannot be null", info.Name, info.ReflectedType.ToString()));
}
}
}
return problems;
}
Any ideas?
I believe what you're looking for is Type.GetNestedTypes()
http://msdn.microsoft.com/en-GB/library/493t6h7t.aspx
I'm not sure, but think that GetProperties method got some flags that can help...

C# working with decorated members

Take this class for example:
public class Applicant : UniClass<Applicant>
{
[Key]
public int Id { get; set; }
[Field("X.838.APP.SSN")]
public string SSN { get; set; }
[Field("APP.SORT.LAST.NAME")]
public string FirstName { get; set; }
[Field("APP.SORT.FIRST.NAME")]
public string LastName { get; set; }
[Field("X.838.APP.MOST.RECENT.APPL")]
public int MostRecentApplicationId { get; set; }
}
How would I go about getting all of the properties that are decorated with the field attribute, get their types, and then assign a value to them?
This is all done with reflection. Once you have a Type object, you can then get its PropertyInfo with myType.GetProperties(), from there, you can get each property's attributes with GetCustomAttributes(), and from there if you find your attribute, you've got a winner, and then you can proceed to work with it as you please.
You already have the PropertyInfo object, so you can assign to it with PropertyInfo.SetValue(object target, object value, object[] index)
You'll need to use Reflection:
var props =
from prop in typeof(Applicant).GetProperties()
select new {
Property = prop,
Attrs = prop.GetCustomAttributes(typeof(FieldAttribute), false).Cast<FieldAttribute>()
} into propAndAttr
where propAndAttr.Attrs.Any()
select propAndAttr;
You can then iterate through this query to set the values:
foreach (var prop in props) {
var propType = prop.Property.PropertyType;
var valueToSet = GetAValueToSet(); // here's where you do whatever you need to do to determine the value that gets set
prop.Property.SetValue(applicantInstance, valueToSet, null);
}
You would just need to invoke the appropriate reflection methods - try this:
<MyApplicationInstance>.GetType().GetProperties().Where(x => x.GetCustomAttributes().Where(y => (y as FieldAttribute) != null).Count() > 0);

Categories

Resources