Importing XML to Objects Recursively - c#

I am trying to write a method which uses reflection in order to get the properties and set their values while traversing XElement:
Lets say I have a class like this which only provides me XML value to be parsed:
class XMLController
{
public string XML
{
get{
return #"<FieldGroup name='People' count='20'>
<Fields>
<Field Name='Jon' LastName='McFly'/>
<Field Name='Michael' LastName='Jackson'/>
</Fields>
</FieldGroup>";
}
}
}
And this is how my Objects look like:
class FieldGroup
{
public string Name {get;set;}
public string Count {get;set;}
public IEnumerable<Field> Fields {get;set;}
}
class Field
{
public string Name {get;set;}
public string LastName {get;set;}
}
The mapper method traverses XElement and since the Node names are matching names with the Objects I am thinking this helps little more but I haven't come up with something really useful. I don't want to pass the type but rather, the method will work with almost every XML passed in with the same format.
All it knows the fact that the XML nodes and attributes are matching names.
This is what I've done but didn't really worked:
class XMLObjectMapper
{
public T Map<T>(XElement element) where T: class, new()
{
T entity = (T) Activator.CreateInstance(typeof(T));
if(element.HasAttributes)
{
MapXMLAttributesToObject<T>(element,entity);
}
if(element.HasElements)
{
foreach (var childElement in element.Elements())
{
//if the child element has child elements as well, we know this is a collection.
if(childElement.HasElements)
{
var property = GetProperty<T>(childElement.Name.LocalName);
property.SetValue(entity,new List<property.PropertyType>());
Map<T>(childElement);
}
else
{
var property = GetProperty<T>(childElement.Name.LocalName);
var type = Activator.CreateInstance(property.PropertyType);
type.Dump();
}
}
}
return entity;
}
private void MapXMLAttributesToObject<T>(XElement element, T entity)
{
foreach(XAttribute attribute in element.Attributes())
{
var property = GetProperty<T>(attribute.Name.LocalName);
property.SetValue(entity,attribute.Value);
}
}
private PropertyInfo GetProperty<T>(string propertyName)
{
return typeof(T).GetProperty(propertyName,BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
}
}

You're on the right track, but as you've noticed, you've got some errors.
The following piece of code doesn't compile because you can't use a value (property.PropertyType) in place of a type name. C# is a statically typed language, so types have to be known at compile time rather than being in variables:
new List<property.PropertyType>()
However, if you use reflection, you can choose the types at runtime. We can do this instead:
Activator.CreateInstance(typeof(List<>).MakeGenericType(collectionElementType))
The other problem that you have is that you can't just call Map<T>(childElement). First of all, T is not the right type -- it's the parent element's type, not the child's. Secondly, the child is actually a collection, and Map<T> doesn't know how to handle collections, only individual objects. We have to loop over the child elements, map onto each single one (calling Map<T> with the type of the elements in the collection -- in your example, Map<Field), and then add them all to the collection. I've made a new version of your Map<T> that works:
public T Map<T>(XElement element) where T : class, new()
{
T entity = (T)Activator.CreateInstance(typeof(T));
if (element.HasAttributes)
{
MapXMLAttributesToObject<T>(element, entity);
}
if (element.HasElements)
{
foreach (var childElement in element.Elements())
{
var property = GetProperty<T>(childElement.Name.LocalName);
// If the child element has child elements as well, we know this is a collection.
if (childElement.HasElements)
{
// Assume collections are of type IEnumerable<T> or List<T>
var collectionElementType = property.PropertyType.GetGenericArguments()[0];
// var collectionValue = new List<collectionElementType>()
var collectionValue = Activator.CreateInstance(typeof(List<>).MakeGenericType(collectionElementType));
foreach (var grandchildElement in childElement.Elements())
{
// var collectionElement = this.Map<collectionElementType>(grandchildElement);
var collectionElement = this.GetType().GetMethod("Map").MakeGenericMethod(collectionElementType).Invoke(this, new object[] { grandchildElement });
collectionValue.GetType().GetMethod("Add").Invoke(collectionValue, new object[] { collectionElement });
}
property.SetValue(entity, collectionValue, null);
}
else
{
// I'm not sure what this should do -- this case doesn't happen in your example.
throw new NotImplementedException();
}
}
}
return entity;
}
It certainly needs some more error handling, and I'm assuming you wanted to do something useful in the case where I threw a NotImplementedException. However, it works on your sample.

Related

How to format this namespace list into a structured object with no duplicates?

So I have a list of objects that contain this namespace property. What I am trying to do is group the list into an object that contains no duplicates and is structured like this:
Core
Util
Log
Fetch
Math
Add
Subtract
The input list would look like this:
[
{
Namespace: "Core.Util",
Name: "Log"
},
{
Namespace: "Core.Util",
Name: "Fetch"
},
{
Namespace: "Core.Math",
Name: "Add"
},
{
Namespace: "Core.Math",
Name: "Subtract"
}
]
What you want to do is to convert a flat list of objects into a recursive hierarchy of nodes, where each node is specified by some unique name within the context of its parent. You've tagged your question data-structures so you are looking for a data model that will make it easy to construct such a hierarchy.
Generalizing a bit, each node then should look like the following:
public abstract partial class HierarchicalNode<TItem, THierarchicalNode>
where THierarchicalNode : HierarchicalNode<TItem, THierarchicalNode>, new()
{
public IDictionary<string, THierarchicalNode> Children { get; private set; }
public ICollection<TItem> Items { get; private set; }
public HierarchicalNode()
{
this.Children = new Dictionary<string, THierarchicalNode>();
this.Items = new List<TItem>();
}
}
Here the node contains:
A list of objects, of type TItem. (You would be using string for this.)
A dictionary of child nodes of the same type. Here we use a dictionary to guarantee that there are no duplicates in the namespace names.
I defined Items to be a list, but if you need to prevent duplication of items as well, you could replace the concrete implementation with a HashSet<TItem>
You'll also need some recursive logic to construct the hierarchy. The following should do the trick:
public partial class HierarchicalNode<TItem, THierarchicalNode>
{
public THierarchicalNode GetOrAddChild(string namespaceName)
{
THierarchicalNode child;
if (!Children.TryGetValue(namespaceName, out child))
child = Children[namespaceName] = new THierarchicalNode();
return child;
}
public void AddObject(IList<string> nodeNames, TItem item)
{
AddObject(nodeNames, 0, item);
}
void AddObject(IList<string> nodeNames, int index, TItem item)
{
if (index >= nodeNames.Count)
Items.Add(item);
else
{
GetOrAddChild(nodeNames[index]).AddObject(nodeNames, index + 1, item);
}
}
}
Here the relevant method is AddObject(IList<string> nodeNames, TItem item) which adds an object that lives inside a nested list of nodes, specified by the list of names.
Finally, since you are interested in namespaces, you would define your specific type as follows:
public class Namespace : HierarchicalNode<string, Namespace>
{
}
And construct a root Namespace as follows:
// Deserialize the JSON shown in the question to an array of objects with Namespace and Name properties
var list = JsonConvert.DeserializeAnonymousType(
jsonString,
new[] { new { Namespace = default(string), Name = default(string) } });
var root = new Namespace();
foreach (var item in list)
{
// Split the Namespace property into an array for recursive processing
root.AddObject(item.Namespace.Split('.'), item.Name);
}
Demo fiddle here.
Assuming you are parsing the JSON using JSON.Net into objects, you can use Linq to group them by their "Namespace" property:
var results = jsonObjects.GroupBy(o => new { Core = o.Namespace.Split('.')[0], Sub = o.Namespace.Split('.')[1] });
This would give you 2 groups (Core.Util and Core.Math) with 2 entries each.
I don't know what you mean by "no duplicates" exactly, but I suggest you take a look at the LINQ Distinct()-operator.

Instantiate an instance of a Type including all object properties and adhere to inheritance

I'm not sure if that title is reflective of the actual question, so let me explain. Is there a way to instantiate a class and recursively instantiate all properties that are classes?
For example :
public class Base
{
public int BaseValue{ get; set;}
}
public class Extended : Base
{
public int ExtendedValue{ get; set;}
public AnotherExtendedClass AnotherClass { get; set;}
}
I would like to create a json payload comprised of an empty instance of Extended with all default values and properties instantiated. And use it like:
string representation = Test.CreateDefaultEmptyJson(Extended);
public static string CreateDefaultEmptyJson(Type type)
{
JsonSerializerSettings settings = new JsonSerializerSettings().Configure();
var defaultInstance= Activator.CreateInstance(type);
return JsonConvert.SerializeObject(defaultInstance, settings);
}
The output does not include the Extended class properties. I get back :
{
"BaseValue":0
}
When I would really like to see ( or something similar ):
{
"BaseValue":0,
{
"ExtendedValue":0,
{
...
}
}
}
I suppose I could recursively iterate all types of Extended and call the default constructor, however, before I go down that road there may be a few lines of code to accomplish the same.
As far as I know there is not a built-in way to do this short of writing your own recursive method.
However, assuming that:
your classes all have parameterless (default) constructors,
the non-primitive properties are all concrete types (not interfaces), and
you don't have any reference loops in your class structure,
then you can create such a method in about a dozen lines of code:
public static string CreateDefaultEmptyJson(Type type)
{
return JsonConvert.SerializeObject(RecursiveCreateInstance(type), Formatting.Indented);
}
public static object RecursiveCreateInstance(Type type)
{
object obj = null;
ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes);
if (ctor != null)
{
obj = ctor.Invoke(null);
foreach (PropertyInfo prop in type.GetProperties())
{
Type propType = prop.PropertyType;
if (prop.CanWrite && propType.IsClass)
{
prop.SetValue(obj, RecursiveCreateInstance(propType));
}
}
}
return obj;
}
Fiddle: https://dotnetfiddle.net/3VMTsC
If the above assumptions don't hold, then things get complicated fast. If you're running into issues where you need an easy way to create fake objects for testing purposes, then you might want to look into using a mocking framework.
This hastily-written class begins to address your question. It returns the settable properties which return reference types and walks through them recursively, creating instances as needed.
It doesn't cover
Indexed properties
Depth of recursion
You may be better off just setting defaults on the properties themselves so that the class won't be created with undesirable nulls.
public class PropertyPopulator
{
public void PopulateProperties(object target)
{
var properties = target.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p => p.PropertyType.IsClass && p.CanWrite && p.CanRead);
foreach (var property in properties)
{
var propertyValue = property.GetValue(target);
if (propertyValue == null)
{
var constructor = property.PropertyType.GetConstructor(new Type[] { });
if (constructor != null)
{
propertyValue = constructor.Invoke(new object[] { });
property.SetValue(target, propertyValue);
PopulateProperties(propertyValue);
}
}
}
}
}

generate code for xml-serialization

I want to create a class to be serialized. However I want the Order-attribute to be explicitely set on every member within my class. So I wrote this code:
public void Process(CodeNamespace code, XmlSchema schema)
{
var types = code.Types.Cast<CodeTypeDeclaration>().Where(x => !x.IsEnum);
foreach (var type in types)
{
foreach(var member in type.Members.Cast<CodeTypeMember>().Select((x, i) => new { Item = x, Order = i }))
{
member.Item.CustomAttributes.Add(new CodeAttributeDeclaration("XmlElementAttribute", ???);
}
}
}
I don´t know how to set the named argument Order to a valid value. I already tried new[] { Order = member.Order } but apparently this doesn´t work at all.
So what I want is something that creates this code:
public class MyClass
{
[XmlElement("MyProp", Order = 0)]
public int Prop1 { get; set; }
}
The solution is quite simple. I compared the process with XmlElement-attributes that were automatically added and noticed, that the attributes name is not XmlElementAttribute but System.Xml.Serialization.XmlElementAttribute. Furthermore - as the Order-parameter is an argument of the XmlElementAttribute-constructor we have to add it as CodeAttributeArgument:
var attr = new CodeAttributeDeclaration("System.Xml.Serialization.XmlElementAttribute");
attr.Arguments.Add(new CodeAttributeArgument("Order", new CodePrimitiveExpression(member.Order)));
member.Item.CustomAttributes.Add(attr);

LINQ Changes Not Recognized When Using Generics

I am currently trying to create a Class which employs generics in order to reduce the amount of work needed for future development. As I add Tables to the LINQ To SQL Designer, certain basic Methods are used in each one. Rather than reproduce them in every Partial Class associated with every new Table, I would like to employ a single generic Class. The issue is that any changes made to the Entities are not recognized and therefore not submitted.
Public Partial Class ABC
{
Public Static Bool Synchronize(string source, string destination)
{
try
{
DataContext destinationDB = DataConnection.Destination(destination);
Table<ABC> destinationABCs = destinationDB.ABCs;
DataContext sourceDB = DataConnection.Destination(source)
Table<ABC> sourceABCs = sourceDB.ABCs;
foreach (ABC ABCCode in sourceABCs)
{
ABC destABCCode = destinationABCs.SingleOrDefault(x => x.Id == ABCCode.Id);
bool same = EntityProcessing.AreIdentical(ABCCode, destABCCode);
if (same == false)
{
destABCCode = (ABC)EntityProcessing.Synchronize(ABCCode, destABCCode);
}
}
ChangeSet test = destinationDB.GetChangeSet(); // Test Line For Debugging
destinationDB.SubmitChanges();
}
return true;
}
}
The next Class is:
Public Static Class EntityProcessing
{
Public Static Bool AreIdentical(Object sourceEntity, Object destinationEntity)
{
if (sourceEntity.GetType() == destinationEntity.GetType())
{
Type t = sourceEntity.GetType();
FieldInfo[] tList = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
foreach (FieldInfo fi in tList)
{
if ((fi.GetValue(sourceEntity) != null ? fi.GetValue(sourceEntity).ToString()
: null) == (fi.GetValue(destinationEntity) != null ?
fi.GetValue(destinationEntity).ToString() : null))
{ continue; }
else
{ return false; }
}
return true;
}
else
{ return false; }
}
Public Static Object Synchronize(Object sourceEntity, Object destinationEntity)
{
if (sourceEntity.GetType() == destinationEntity.GetType())
{
Type t = sourceEntity.GetType();
FieldInfo[] tList = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
foreach (FieldInfo fi in tList)
{
fi.SetValue(destinationEntity, fi.GetValue(sourceEntity));
}
}
return destinationEntity;
}
}
I have tried modifying the EntityProcessing.Synchronize method into a Void method as well. Neither works. Both will return the correct Entity with the Fields set to the appropriate results. The issue lies in the fact that LINQ does not recognize the Entities as having changed.
If I add a temporary line of ChangeSet test = destinationDB.GetChangeSet();, the Updated count is zero. The loss appears to be in the conversion to Objects.
I have tried setting the Parameter Type to ABC on the EntityProcessing.Synchronize() method and modifying a Field, and the Updated count in test is correct. How do I resolve this?
How do I submit the updated entities to the database or rather, how do I get LINQ to recognize these entities are being changed and needing an update?
Do you mean: Public Static Bool Synchronize<ABC>(string source, string destination) with "ABC" as the generic type?
However, I don't think your .ABCs will work that simply. You may have to use reflection to get at the proeprty with that particular name. For example, first use reflection to get the name of the type parameter (ABC), and then use reflection to get the table field from the data source based on this type name.

ASP.Net MVC 2 Controller's TryValidate doesn't validate the List<> items within the model

How do you get a model's validation to also validate child objects in a generic list property.
I have a model that I'm trying to validate, this is not what's being posted to the server, but a composite of some information posted, and information already on the server... for example.
...
public class A {
[Required]
public string Property1 { get; set; }
}
...
public class B {
public List<A> Values { get; set; }
}
...
if (!TryValidateModel(instanceofB))
{
//this should fire, as one of A inside B isn't valid.
return View(instanceofB);
}
When I try to validate the model instance of B, it won't validate the Values collection for their validation attributes.
The TryValidateModel method only goes down one level so it only checks for Validation attributes on the object of type B, not on its nested objects. One way to overcome this is to define your own implementation of a ValidationAttribute:
public class ListValidationAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
IEnumerable enumerable = value as IEnumerable;
// If the input object is not enumerable it's considered valid.
if (enumerable == null)
{
return true;
}
foreach (object item in enumerable)
{
// Get all properties on the current item with at least one
// ValidationAttribute defined.
IEnumerable<PropertyInfo> properties = item.GetType().
GetProperties().Where(p => p.GetCustomAttributes(
typeof(ValidationAttribute), true).Count() > 0);
foreach (PropertyInfo property in properties)
{
// Validate each property.
IEnumerable<ValidationAttribute> validationAttributes =
property.GetCustomAttributes(typeof(ValidationAttribute),
true).Cast<ValidationAttribute>();
foreach (ValidationAttribute validationAttribute in
validationAttributes)
{
object propertyValue = property.GetValue(item, null);
if (!validationAttribute.IsValid(propertyValue))
{
// Return false if one value is found to be invalid.
return false;
}
}
}
}
// If everything is valid, return true.
return true;
}
}
Now List<A> can be validated using the attribute:
public class B
{
[ListValidation]
public List<A> Values { get; set; }
}
I haven't tested performance for the above approach thoroughly but if in your case that turns out to be a problem, an alternative approach is to use a helper function:
if (!ValidateB(instanceofB))
{
//this should fire, as one of A inside B isn't valid.
return View(instanceofB);
}
...
public bool ValidateB(B b)
{
foreach (A item in b.Values)
{
if (!TryValidateModel(item))
{
return false;
}
}
return true;
}
I had a similar issue that I fixed by avoiding the call to TryValidate altogether. The reason I called TryValidate was because I needed to do make some changes on my model and then do the validation. I ended up creating an interface for the model and replaced the default model binder with one that recognizes the interface and calls my method. This all happens before the framework calls validate the first time (which is recursive).

Categories

Resources