Building Xml with XElement dynamically through recursion - c#

I'm new to linq to Xml.
I have a recursive method which get as parameter XElement root which should hold the XML data in a way it will represent the correlating subtree root of a given recursion depth.
void recursiveMethod(XElement root);
To be more specific,also look at this XML example:
<start>
<Class>
<Worker>
<Name> Dan </Name>
<Phone> 123 </Phone>
<Class>
<Address>
<Street> yellow brick road </Street>
<Zip Code> 123456 </Zip Code>
</Address>
</Class>
</Worker>
</Class>
...
</start>
As you can imagine, Name is value type whereas Address is a class reference.
The Xml information should be added dynamically through reflection ( in top down approach).
To make the long story short imagine that I'm in the middle of investigating Worker Class and reached Address Class and want to "drill down", so I want to call my recursive method with the right reference of child nodes of the current Worker class as the new XElement root, so I will be able to add to it what I found by reflection in the Address Class one recursion depth below.
Note that this reference should be of XElement type.
How can I do that?
EDIT: If you have another idea of doing all this stuff but not with XElement I'll be happy to hear about also, although I prefer it with XElement parameter.
Another issue:
I've started implementing it in a naive way like iterating through all fields (variable of FieldInfo[]), and if I had encounterd value type(IsValueType) I was doing something like
root.Add(new XElement("Field",
new XElement("Type", ...),
new XElement("Variable Name", ...),
new XElement("Value", ...)));
So ,just for general knowledge:
1. Was there a way to get only the reference of a node to its decendants ,so that in lower recursion level I'll be able to do another root.Add(...) as above but this root will be a reference to children of previous root? (Which means doing the whole operation without Linq syntax)
2.I've managed to get private fields value through reflection without working with properties, is it problematic? Should I always take values through properties in reflection?

This extension method will build XElement in required format:
public static class Extensions
{
public static XElement ToXml<T>(this T obj)
{
Type type = typeof(T);
return new XElement("Class",
new XElement(type.Name,
from pi in type.GetProperties()
where !pi.GetIndexParameters().Any()
let value = (dynamic)pi.GetValue(obj, null)
select pi.PropertyType.IsPrimitive ||
pi.PropertyType == typeof(string) ?
new XElement(pi.Name, value) :
Extensions.ToXml(value)
)
);
}
}
What happens here:
We get public properties of passed object (you can add BindingFlags to filter properties).
Next I verify if property has index parameters and skip such properties.
Next I get property value as dynamic object. It's important, otherwise property value type will be inferred as object during recursive call of ToXml<T> method.
I check if property type is primitive type (int, byte, long, single, double, etc) or string
For primitive types we write element with property value. For other types (complex) we recursively start building XElement
Usage:
Worker worker = new Worker()
{
Name = "Serge",
Phone = "911",
Address = new Address() { Street = "Elm street", ZipCode = 666 }
};
XElement xml = worker.ToXml();
Result:
<Class>
<Worker>
<Name>Serge</Name>
<Phone>911</Phone>
<Class>
<Address>
<Street>Elm street</Street>
<ZipCode>666</ZipCode>
</Address>
</Class>
</Worker>
</Class>
BUT you should be careful with situations when two objects refer each other (infinite recursion will happen in this case)
In this case you can use something like dictionary or hashset to store all objects which already exist in your xml:
Type type = obj.GetType();
if (set.Contains(obj))
return new XElement("Class", new XAttribute("name", type.Name));
set.Add(obj);
return new XElement("Class", ...);

Related

Customize the xml generated for c# instances

I am using XmlSerializer in c# to serialize a class instance to xml. By default, if I serialize the instance of following class:
public class XmlTest
{
public bool b1;
}
the bool field is represented as <b1>false</b1>
I have specific requirement that the value of bool fields be represented as elements as in <false> and <true> instead of text content. I have similar requirements for enum fields as well.
I have tried using attributes that control XML serialization but couldn't find one that meets my requirement. I am fairly new to c# and was wondering the best way to achieve this.
You can't do that with the XML Serializer. You can use LINQ to XML instead:
public XElement Serialize(XmlTest t){
return new XElement("XmlTest",
new XElement(t.b1 ? "true" : "false"));
}
One hint on using LINQ to XML in an code generation setting: XElement has a constructor that looks very simple:
public XElement(
XName name,
Object content
)
but that content parameter can be almost anything. That includes
Any type that implements ToString
Any type that implements IEnumerable
So, for example:
XElement xmlTree2 = new XElement("Root",
from el in xmlTree1.Elements()
where((int)el >= 3 && (int)el <= 5)
select el
);
could produce
<Root>
<Child3>3</Child3>
<Child4>4</Child4>
<Child5>5</Child5>
</Root>

deserialize xml node with unknown child nodes

I have some xml that looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<response>
<video key="8bJ8OyXI">
<custom>
<legacyID>50898311001</legacyID>
</custom>
<date>1258497567</date>
<description>some description</description>
<duration>486.20</duration>
<md5>bc89fde37ef103db26b8a9d98065d006</md5>
<mediatype>video</mediatype>
<size>99416259</size>
<sourcetype>file</sourcetype>
<status>ready</status>
<views>0</views>
</video>
</response>
I am using XmlSerializer to serialize the xml to class objects, and would prefer to stick with it if possible since everything else works just fine. The node custom is just custom metadata added to the video, and pretty much anything could potentially end up in there (only strings, just a name and value). I used xsd.exe to generate class objects from my xml, which generated a unique class for the <custom> tag with just one ulong property for the legacyID value. The thing is, potentially any arbitrary number of values could be there and I can't and don't need to account for them all (but I may need to read particular values later).
Is it possible to set up the Video.Custom property in my class so that the serializer can deserialize those values into say something like a Dictionary<string, string>? I don't need type information for those particular values, saving the node names + values are more than enough for my purposes.
You can handle UnknownElement event and there deserialize custom element to your dictionary
serializer.UnknownElement += (s, e) =>
{
if (e.Element.LocalName == "custom" && e.ObjectBeingDeserialized is Video)
{
Video video = (Video)e.ObjectBeingDeserialized;
if (video.Custom == null)
{
video.Custom = new Dictionary<string, string>();
}
foreach (XmlElement element in e.Element.OfType<XmlElement>())
{
XmlText text = (XmlText)element.FirstChild;
video.Custom.Add(element.LocalName, text.Value);
}
}
};

How to read a dictionary element from XML?

I'm trying to obtain the relevant XML attribute based on the value but I cannot get it to work.
What I am trying to achieve is based on the returned value I want to output the elements name.
Where am I going wrong?
Here's my code so far:
XML:
<addresses>
<address name="house 1">No 1</ipaddress>
<address name="house 2">Flat 3</ipaddress>
<address name="house 3">Siccamore Drive</ipaddress>
</addresses>
C#:
string configPath = _AppPath + #"\HouseAddresses.xml";
XDocument addressXdoc = XDocument.Load(configPath);
XElement addressXmlList = addressXdoc.Element("traplistener");
foreach (XNode node in addressXmlLst.Element("addresses").Descendants())
{
PropertyList = ("string")node.Attribute("name");
}
The XNode type can be seen as a "base". As the documentation states, it represents the abstract concept of a node (element, comment, document type, processing instruction, or text node) in the XML tree. Adding a Attribute property to a text, for example, does not really make sense in the XML context. For that reason, the XNode type does not provide a Attribute property. The XElement type, however, does. Therefore, changing your foreach loop to the version bellow, should do the trick:
foreach (XElement element in addressXmlLst.Element("addresses").Descendants())
{
PropertyList = ("string")element.Attribute("name");
}
A "random" note on your code: since XElement extends XNode the elements returned by Descendants() are correctly converted; for this reason, your problem appears to come from the fact that XNode does not expose a Attribute property, when, in fact, it originates from an unnecessary type conversion.
As an improvement, I would suggest the following:
foreach (XElement element in addressXmlLst.Element("addresses").Descendants())
{
//check if the attribute is really there
//in order to prevent a "NullPointerException"
if (element.Attribute("name") != null)
{
PropertyList = element.Attribute("name").Value;
}
}
In addition to Andrei's answer you can also convert the xml to a dictionary directly via LINQ:
var dictionary = addressXmlLst.Element("addresses").Descendants()
.ToDictionary(
element => element.Attribute("name").Value, // the key
element => element.Value // the value
);

How do I turn a deserialized XML object (.NET) into a single collection of dot separated named key values?

To start, I am constrained to .NET 2.0 so LINQ is not an option for me (though I would be curious to see a LINQ solution as fodder for pushing to move to .NET 3.5 for the project if it is easy).
I have an XSD that is turned into a set of C# classes via xsd.exe at build time. At runtime, an XML file is loaded and deserialized into the C# classes (validation occurs at this time). I need to then turn that in-memory configuration object (including the default values that were populated during import of the XML file) into a dictionary of key value pairs.
I would like the dictionary key to be a dot separated path to the value. Attribute values and element text would be considered values, everything else along the way a key into that.
As an example, imagine the following XML file:
<rootNode>
<foo enabled="true"/>
<bar enabled="false" myAttribute="5.6">
<baz>Some Text</baz>
<baz>Some other text.</baz>
</bar>
</rootNode>
would turn into a dictionary with keys like:
"rootNode.foo.enabled" = (Boolean)true
"rootNode.bar.enabled" = (Boolean)false
"rootNode.bar.myAttribute" = (Float)5.6
"rootNode.bar.baz" = List<String> { "Some Text", "Some other text." }
Things of note are that rootNode is left off not because it is special but because it had no text or attributes. Also, the dictionary is a dictionary of objects which are typed appropriately (this is already done in deserialization, which is one of the reasons I would like to work with the C# object rather than the XML directly).
Interestingly, the objects created by xsd.exe are already really close to the form I want. The class names are things like rootNodeFoo with a float field on it called myAttribute.
One of the things I have considered but am not sure how to go about are using reflection to iterate over the object tree and using the names of the classes of each object to figure out the name of the node (I may have to tweak the casing a bit). The problem with this is that it feels like the wrong solution since I already have access to a deserializer that should be able to do all of that for me and much faster.
Another option would be using XSLT to serialize the data directly to a format that is how I want. The problem here is that my XSLT knowledge is limited and I believe (correct me if I am wrong) I will lose typing on the way (everything will be a string) so I will have to essentially deserialize once again by hand to get the types back out (and this time without XSD validation that I get when I use the .NET deserializer).
In case it matters, the calls I am using to get the configuration object populated from an XML file is something like this:
var rootNode = new XmlRootAttribute();
rootNode.ElementName = "rootNode";
rootNode.Namespace = "urn:myNamespace";
var serializer = new XmlSerializer(typeof(rootNode), rootNode);
using (var reader = new StringReader(xmlString))
{
var deserializedObject = (rootNode)serializer.Deserialize(reader);
}
First observation: using the object graph is not the best place to start to generate a dot representation. You're talking about nodes which have names and are in a well-defined hierarchy and you want to produce some kind of dot notation from it; the xml DOM seems to be the best place to do this.
There are a few problems with the way you describe the problem.
The first is in the strategy when it comes to handling multiple elements of the same name. You've dodged the problem in your example by making that dictionary value actually a list, but suppose your xml looked like this:
<rootNode>
<foo enabled="true">
<bar enabled="false" myAttribute="5.6" />
<bar enabled="true" myAttribute="3.4" />
</foo>
</rootNode>
Besides foo.enabled = (Boolean)true which should be fairly obvious, what dictionary keys do you propose for the two myAttribute leaves? Or would you have a single entry, foo.bar.myAttribute = List<float> {5.6, 3.4}? So, problem #1, there's no unambiguous way to deal with multiple similarly-named non-leaf nodes.
The second problem is in selecting a data type to do the final conversion at leaf nodes (i.e. attribute or element values). If you're writing to a Dictionary<string, object>, you will probably want to select a type based on the Schema simple type of the element/attribute being read. I don't know how to do that, but suggest looking up the various uses of the System.Convert class.
Assuming for the moment that problem #1 won't surface, and that you're ok with a Dictionary<string, string> implementation, here's some code to get you started:
static void Main(string[] args)
{
var xml = #"
<rootNode>
<foo enabled=""true"">
<bar enabled=""false"" myAttribute=""5.6"" />
<baz>Text!</baz>
</foo>
</rootNode>
";
var document = new XmlDocument();
document.LoadXml(xml);
var retVal = new Dictionary<string, string>();
Go(retVal, document.DocumentElement, new List<string>());
}
private static void Go(Dictionary<string, string> theDict, XmlElement start, List<string> keyTokens)
{
// Process simple content
var textNode = start.ChildNodes.OfType<XmlText>().SingleOrDefault();
if (textNode != null)
{
theDict[string.Join(".", keyTokens.ToArray())] = textNode.Value;
}
// Process attributes
foreach (XmlAttribute att in start.Attributes)
{
theDict[string.Join(".", keyTokens.ToArray()) + "." + att.Name] = att.Value;
}
// Process child nodes
foreach (var childNode in start.ChildNodes.OfType<XmlElement>())
{
Go(theDict, childNode, new List<string>(keyTokens) { childNode.Name }); // shorthand for .Add
}
}
And here's the result:
One approach would be to implement a customer formatter and slot it into the standard serialization pattern, create a class that implements IFormatter i.e. MyDotFormatter
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.iformatter.aspx
then implement as below
Stream stream = File.Open(filename, FileMode.Create);
MyDotFormatter dotFormatter = new MyDotFormatter();
Console.WriteLine("Writing Object Information");
try
{
dotFormatter.Serialize(stream, objectToSerialize);
}
catch (SerializationException ex)
{
Console.WriteLine("Exception for Serialization data : " + ex.Message);
throw;
}
finally
{
stream.Close();
Console.WriteLine("successfully wrote object information");
}

XML comparer C#

I want to compare 2 XML files.
Looks easy if both of them have an identical structure. But not in my case :(
My files looks like:
<root>
<t>
<child1>
<cc1>val</cc1>
<cc2>val</cc2>
......
</child1>
<child2>
<cc1>val</cc1>
<cc2>val</cc2>
......
</child2>
<child2>
<cc1>val</cc1>
<cc2>val</cc2>
......
</child2>
.......
<child3>
<cc1>val</cc1>
<cc2>val</cc2>
......
</child3>
....
</t>
<t>
...
</t>
.....
</root>
And they could have any numbers of childes, and childes of childes...
The task is
To compare only one defined block. I need search it for value of 1st child's child (child1.cc1.value in this example)
During the comparetion some nodes could be skipped (the names of skipped nodes stored somewhere, for example, in strings array)
It is possible to have multiple identical nodes like . And if child2 isn't ignored, then I need to make sure they are the same amount, and they all coincide with the corresponding second file. So there could be next situation:
1st file contains:
<child2><cc1>1</cc1>...</child2>
<child2><cc1>3</cc1>...</child2>
<child2><cc1>2</cc1>...</child2>
2st file contains:
<child2><cc1>2</cc1>...</child2>
<child2><cc1>1</cc1>...</child2>
<child2><cc1>3</cc1>...</child2>
And that means they are corresponds each other.
So they could be in the random order.
Now I can't make a decision how to realize this algorithm. I suggested to use DataSet objects, but this XML-structure looks too difficult for simply using DataTables, dataRows and etc..
Now I'm trying XmlNodes. But I haven't realized that part where I have several identical nodes with different data in random order.
Any ideas?
How large are your XML files? And how complex is the structure in reality?
If not too large or complex then I would recommend parsing the whole file into a class structure and then performing your validation on the properties of the classes. For example (pseudocode)...
xmlClass file1 = new xmlClass(file1info);
xmlClass file2 = new xmlClass(file2info);
//Custom classes have now parsed XML files in whichever way you like
if (file1.numberOfChildren != file2.numberOfChildren)
{
//comparison fail
}
elseif (!file1.orderOfChildrenSame(file2))
{
//comparison fail
}
else
{
//comparison success
}
Obviously the exact implementation of the methods and properties of your xmlClass will depend on your exact requirements.
XmlClass may be of the rough layout...
using System;
using System.Collections.Generic;
using System.Xml;
public class XmlClass
{
private XmlDocument _xmlDoc;
private List<ChildClass> _children As New List<ChildClass>();
public XmlClass(FileInfo fil){
_xmlDoc = New XmlDocument();
_xmlDoc.Load(fil.FullName);
ParseChildren();
_xmlDoc = Nothing;
}
private void ParseChildren(){
XmlNodeList ndl = _xmlDoc.SelectNodes("/root/t") //select all <t>s
foreach (xmlNode nodT in ndl.Nodes){
foreach (xmlNode nodChild in nodT.ChildNodes()){
_children.Add(new ChildClass(nodChild));
}
}
// Now _children contains all child nodes of <t>s and can be worked with logically
}
public int numberOfChildren
{
get {return _children.Count();}
}
}
You will obviously need to implement ChildClass - which may in turn contain a collection of ChildClass itself (allowing the hierarchy you describe). You will also need to implement the other validation methods as you require. Also you may need to implement other classes to represent other node types within the document which you are interested in.
Don't parse more than you need to in order to validate! - It depends what your end goal is.
PS
I would also suggest that this XML format is not very "nice" in terms of the <child1>, <child2> set-up. It would be much more XMLesque to have <child id="1">, <child id="2"> etc. As presumably <child1> and <child2> are essentially the same type of node...

Categories