In the app I'm currently working with the user is allowed to store a lot of settings divided in different categories in XML files.
The XML could look like
<settings>
<setting2>
<setting3>
<value1>foo</value1>
<value2>bar</value2>
</setting3>
<value3>A list of strings</value3>
</setting2>
<setting4>
<value4>An integer</value4>
</setting4>
</settings>
The thing I'm confused about is how to best structure the class containing this information, I started out by doing a recursive class containing a list of categories and values, and now I remembered that there is a tree collection class in Java and something like that would be the best solution?
What do you guys recommend?
Why not just load the entire XML file into an XPathDocument? It's easy to use and quite speedy.
You could access individual settings like so:
XPathDocument xmldoc = new XPathDocument(settingsFile);
XPathNavigator nav = xmldoc.CreateNavigator();
foreach (XPathNavigator node in nav.Select("settings/setting2/setting3"))
{
...
}
I would stand for more OO approach.
public abstract class SettingAbstract
{
public abstract List<Setting> Children {get;set;}
public abstract List<object> Values {get;set}
public XmlNode SaveMe();
}
and after concrete implementation for every setting type
public Setting2 : SettingAbstract
{
//concrete implementation
}
public Setting3 : SettingAbstract
{
//concrete implementation
}
......
......
Something like this.
Related
I'm not sure if this is the best way to do this, but I am following an example that worked on some other projects for importing an XML file and treating it like a class. So content like:
<Book>
<Author>Johnny Appleseed</Author>
</Book>
Can be accessed in C# using:
Book.Author
The example I've used creates a public class that inherits XTypedElement, uses its .Load method to open an XML, and then uses LINQ to create properties. So for example,
public class Book: XTypedElement, IXMetaData
{
private static readonly Dictionary<XName, Type> authorDictionary = new Dictionary<XName, Type>();
private static readonly ContentModelEntity authorContent;
static Book()
{
authorDictionary .Add(XName.Get("Author", ""), typeof(string));
authorContent= new SequenceContentModelEntity(new NamedContentModelEntity(XName.Get("Author", "")));
}
public string Author
{
get
{
var x = Element(XName.Get("Author", ""));
return XTypedServices.ParseValue<string>(x, XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.String).Datatype);
}
set
{
SetElement(XName.Get("Author", ""), value, XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.String).Datatype);
}
}
public static Book Load(string xmlFile)
{
return File.Exists(xmlFile) ? XTypedServices.Load<Book>(xmlFile) : null;
}
}
This seems to work really well. Classes representing complex types can be nested using a similar structure. However, all of this breaks as soon as the root node of the XML has a namespace.
<Book xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > THIS WORKS
<Book xmlns="ThisIsAValue" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > THIS DOESN'T
A TargetInvocationException is thrown, with the message "Element is not an instance of Type Book".
There is an XSD that maps to this class, maybe that isn't set up right. I'm not sure. It'd be nice to know where to start looking.
Any ideas?
The problem ended up being every instances of XName used throughout my class (which was a lot).
// XName.Get is overloaded. If you pass two parameters, the second one is expected to be the namespace of the XML.
XName.Get("Author", "")));
Furthermore, my effort seems to be wasted! I just found the sample I was using is based on an open source project found here, where they provide an executable to generate these classes for you. Done and done.
I'm having a bit of a head scratching moment here, as I think I'm doing this correctly!
I need to create an xml file as below, (I've left out the namespace declarations)
<races>
<race racename="race one">
<horse>
<name>Silver</name>
<age>6</name>
</horse>
</race>
</races>
Class races is a collection of class race, and class race is a collection of class horse. Below is the relevant code for race class which I have and is causing the problem (I think at least).
[Serializable]
[XmlType("race")]
public class Race : CollectionBase
{
private string _raceName;
[XmlAttribute("racename")]
public string RaceName
{
get
{
return this._raceName;
}
set
{
this._raceName = value;
}
}
I have the xml file building as expected EXCEPT the attribute racename is not being serialized. It is definitely being assigned to the race object before serialization. Any thoughts? I'm obviously missing something somewhere but I'm not sure how I'd even test where it's failing. Any help would be greatly appreciated!
Eoin.
In classes that implement IEnumerable, only collections are serialized, not public properties.
Use the Horse collection inside the Race class and remove :CollectionBase
[Serializable]
[XmlType("race")]
public class Race
{
[XmlElement("horse")]
public List<Horse> Horses { get; set; }
[XmlAttribute("racename")]
public string RaceName
{
get;
set;
}
public Race()
{
Horses = new List<Horse>();
}
}
Result
<race xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" racename="race one">
<horse>
<Name>Silver</Name>
<Age>6</Age>
</horse>
</race>
I suggest you to create a test console application in the solution and test code like this http://pastebin.com/bP340WmR if it works fine create a collections of objects and try to serialize. Doing it step by step will help to understand where exactly the problem is.
I am developing a library to convert HTML document as word document. This is done by traversing through the HTML document and process the HTML elements one by one. There are family of classes to process each HTML tag.
public abstract class DocxElement
{
public void Process();
}
public class DocxTable : DocxElement
{
public override void Process(){}
}
public class DocxDiv : DocxElement
{
public override void Process(){}
}
The above classes are responsible to process its html counterpart. So whenever I expand the library to support an additional html tag, I will just create a sub class from DocxElement. The html parser uses a factory class to generate concerate DocxElement class whenever it meets an HTML tag.
public class ElementFactory
{
public DocxElement Resolve(string htmlTag)
{
switch(htmlTag)
{
case "table":
return new DocxTable();
case "div":
return new DocxDiv();
}
}
}
Now I feel it violates the Open closed principle. I prefer not to use reflection just because of design patterns require that. So I created a singleton dictionary to register the element classes.
Dictionary<string, Func<DocxElement>> doc;
doc.Add("table",()=>{ new DocxTable();});
Finally I was able to eliminate the switch statement. Still I need to add elements to the dictionary when I create a new sub class.
Is there any better method to do this? Please advice.
I would say your Dictionary method is fine. Anything else trying to make this generic will lose the static compile time checking. If you're ready to sacrifice the compile time checking, you can use reflection to make this code generic.
public class ElementFactory
{
public DocxElement Resolve(string htmlTag)
{
var type = Type.GetType(string.Format("{0}.Docx{1}",
typeof(ElementFactory).Namespace,
CultureInfo.CurrentCulture.TextInfo.ToTitleCase(htmlTag)));
return (DocxElement)Activator.CreateInstance(type);
}
}
How to use:
ElementFactory factory = new ElementFactory();
var table = factory.Resolve("table");//Works
var div = factory.Resolve("div");//Works
var span = factory.Resolve("span");//Explodes!!
As you can see this can fail a runtime for several reasons. Type not found, Type found but no public parameterless constructor, Type found but it doesn't derive from DocxElement, etc etc..
So it is better you can stick with Dictionary option IMO.
I am currently working on a project where I need to create objects at runtime based on a configuration file.
Project example
Here is a simplified example of the project I am working on.
Every object created must implement a specific interface, let it be called the IObjectInterface for the example purpose :
public interface IObjectInterface
{
void DoSomething();
}
Let's assume that I have several classes that implement this interface, each having a specific implementation, and multiple other properties relevant to the specific type :
public class SimpleObject : IObjectInterface
{
public void DoSomething()
{
Console.WriteLine("SimpleObject did something");
}
}
public class ComplexObject : IObjectInterface
{
public string ObjectName { get; set; }
public void DoSomething()
{
Console.WriteLine("The ComplexObject named {0} did something", this.ObjectName);
}
}
public class VeryComplexObject : IObjectInterface
{
public string ObjectName { get; set; }
public bool CanDoSomething { get; set; }
public void DoSomething()
{
if (this.CanDoSomething)
{
Console.WriteLine("The ComplexObject named {0} did something", this.ObjectName);
}
else
{
Console.WriteLine("The ComplexObject named {0} tried to do something, but was not allowed to", this.ObjectName);
}
}
}
I need to be able to create a IObjectInterface object corresponding to a specific Id using the ObjectBuilder class :
public class ObjectBuilder
{
IObjectInterface BuildObject(string objectId)
{
// Build object here based on provided Id
}
}
What I am looking for is a way to specify, in a configuration file, the correct implementation of IObjectInterface that should be created, and the parameter values associated to the specific type.
The ideal configuration file should look like this :
<objects>
<object id="Simple">
<objectInterface type="SimpleObject" />
</object>
<object id="Complex">
<objectInterface type="ComplexObject">
<ObjectName value="MyObject" />
</objectInterface>
</object>
</objects>
I think I should be able to manage the object instantiation part, but I don't really see how I can manage the initialization of the instances with clean code.
Do you have some leads on how I can implement such a configuration file ?
How can I easily create the final instances of the needed objects and initialize them with the parameters provided in the configuration file ?
Are there any frameworks that could help me implementing a solution to my problem ?
Sounds like IOC for me. StructureMap NInject Microsoft Extensibility Framework
You should be able to use assemblyInstance.GetType(string) to resolve the types (you might need to prefix with the namespace) - after that you have two choices:
1 use Activator.CreateInstance(resolvedType) to instantiate and reflection over the properties - presumably hard-coding a few PropertyType approaches for parsing string, int, etc
but that sounds like work
2 change the format of the config file such that the inner xml block is compatible with XmlSerializer - then you can just grab the inner xml and run it through
var ser = new XmlSerializer(resolvedType);
var obj = (IObjectInterface) ser.Deserialize(...);
(using either the subtree (ReadSubTree) reader if you already have an XmlReader, or XmlReader.Create(new StringReader(xml)) if you have the xml as a string.
For example xml (thinking of XmlSerializer) maybe:
<object id="Complex" type="ComplexObject">
<ComplexObject>
<ObjectName>MyObject</ObjectName>
</ComplexObject>
</object>
You may want to use reflection to create an instance of the type provided in the configuration.
I did something very similar in Java, and I wrote up a detailed explanation of the reflection-based approach I took. I don't know C#, but apparently it is similar to Java, so my Java-centric explanation might be useful to you.
You can find the explanation in Part III ("The Config4JMS Case Study") of the "Config4* Practical Usage Guide" manual that is provided with Config4*. However, before reading that, I suggest you read Chapters 2 and 3 of the "Config4* Getting Started Guide" to get an overview of the configuration-file syntax and the programming API. Doing that will make it easier to read the case study.
I've got a simple class that inherits from Collection and adds a couple of properties. I need to serialize this class to XML, but the XMLSerializer ignores my additional properties.
I assume this is because of the special treatment that XMLSerializer gives ICollection and IEnumerable objects. What's the best way around this?
Here's some sample code:
using System.Collections.ObjectModel;
using System.IO;
using System.Xml.Serialization;
namespace SerialiseCollection
{
class Program
{
static void Main(string[] args)
{
var c = new MyCollection();
c.Add("Hello");
c.Add("Goodbye");
var serializer = new XmlSerializer(typeof(MyCollection));
using (var writer = new StreamWriter("test.xml"))
serializer.Serialize(writer, c);
}
}
[XmlRoot("MyCollection")]
public class MyCollection : Collection<string>
{
[XmlAttribute()]
public string MyAttribute { get; set; }
public MyCollection()
{
this.MyAttribute = "SerializeThis";
}
}
}
This outputs the following XML (note MyAttribute is missing in the MyCollection element):
<?xml version="1.0" encoding="utf-8"?>
<MyCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>Hello</string>
<string>Goodbye</string>
</MyCollection>
What I want is
<MyCollection MyAttribute="SerializeThis"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>Hello</string>
<string>Goodbye</string>
</MyCollection>
Any ideas? The simpler the better. Thanks.
Collections generally don't make good places for extra properties. Both during serialization and in data-binding, they will be ignored if the item looks like a collection (IList, IEnumerable, etc - depending on the scenario).
If it was me, I would encapsulate the collection - i.e.
[Serializable]
public class MyCollectionWrapper {
[XmlAttribute]
public string SomeProp {get;set;} // custom props etc
[XmlAttribute]
public int SomeOtherProp {get;set;} // custom props etc
public Collection<string> Items {get;set;} // the items
}
The other option is to implement IXmlSerializable (quite a lot of work), but that still won't work for data-binding etc. Basically, this isn't the expected usage.
If you do encapsulate, as Marc Gravell suggests, the beginning of this post explains how to get your XML to look exactly like you describe.
http://blogs.msdn.com/youssefm/archive/2009/06/12/customizing-the-xml-for-collections-with-xmlserializer-and-datacontractserializer.aspx
That is, instead of this:
<MyCollection MyAttribute="SerializeThis"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Items>
<string>Hello</string>
<string>Goodbye</string>
<Items>
</MyCollection>
You can have this:
<MyCollection MyAttribute="SerializeThis"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>Hello</string>
<string>Goodbye</string>
</MyCollection>
As Neil Whitaker suggests and in case his link dies..
Create an inner collection to store the strings and apply the XmlElement attribute to mask the collection name. Produces the same xml output as if MyCollection inherited from Collection, but also serializes attributes on the parent element.
[XmlRoot("MyCollection")]
public class MyCollection
{
[XmlAttribute()]
public string MyAttribute { get; set; }
[XmlElement("string")]
public Collection<string> unserializedCollectionName { get; set; }
public MyCollection()
{
this.MyAttribute = "SerializeThis";
this.unserializedCollectionName = new Collection<string>();
this.unserializedCollectionName.Add("Hello");
this.unserializedCollectionName.Add("Goodbye");
}
}
I've been fighting with the same issue Romaroo is (wanting to add properties to the xml serialization of a class that implements ICollection). I have not found any way to expose properties that are in the collection class. I even tried using the XmlAttribute tag and making my properties show up as attributes of the root node, but no luck there either. I was however able to use the XmlRoot tag on my class to rename it from "ArrayOf...". Here are some references in case you're interested:
MilkCarton.com
Microsoft Forum Posting
Diranieh - look halfway down
Sometimes you just want to do what you just want to do; the Framework be damned.
I posted an answer here Property of a List<T> Not Deserialized
that does what the OP wanted to do.