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.
Related
I've been searching on google about this for an hour but I think I don't use the right word because I can't find a very simple example of what I'm trying to do. People always use complexe structure like List or derived object in the samples.
All I want to do is to XMLSerialize my main object called SuperFile to a file. This SuperFile class contains 2 members and these 2 members are not serialized so the resulting XML file is empty (containing only the header).
Here is my code, what am I doing wrong?
SuperFile
public class SuperFile
{
private NetworkInfo _networkInfo;
private Planification _planification;
public NetworkInfo NI
{
get
{
return _networkInfo;
}
}
public Planification Planif
{
get
{
return _planification;
}
}
}
NetworkInfo and Planification are very normal class with mostly double member and they serialize perfectly on their own if I want. But now, I want them to serialize inside the SuperFile object.
Finally, here is my code to do the serialization
public void Save(string strFilename)
{
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(typeof(ExoFile));
TextWriter WriteFileStream = new StreamWriter(strFilename);
x.Serialize(WriteFileStream, this);
WriteFileStream.Close();
}
If I put this inside SuperFile, it get serialized but the 2 other member gets skipped. I think it get serialize since it's not a complex type...
public int _nDummy;
Hope it's clear!
Thanks!
XMLSerializer has some limitations, one of which is to require a setter. (It also doesn't serialise private fields, indexers..). It's not an obvious gotcha, and has had me scratching my head in the past :)
here's an answer with some details - Why isn't my public property serialized by the XmlSerializer?
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.
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.
Here's a fictitious example of the problem I'm trying to solve. If I'm working in C#, and have XML like this:
<?xml version="1.0" encoding="utf-8"?>
<Cars>
<Car>
<StockNumber>1020</StockNumber>
<Make>Nissan</Make>
<Model>Sentra</Model>
</Car>
<Car>
<StockNumber>1010</StockNumber>
<Make>Toyota</Make>
<Model>Corolla</Model>
</Car>
<SalesPerson>
<Company>Acme Sales</Company>
<Position>
<Salary>
<Amount>1000</Amount>
<Unit>Dollars</Unit>
... and on... and on....
</SalesPerson>
</Cars>
the XML inside SalesPerson can be very long, megabytes in size. I want to deserialize the tag, but not deserialize the SalesPerson XML element instead keeping it in raw form "for later on".
Essentially I would like to be able to use this as a Objects representation of the XML.
[System.Xml.Serialization.XmlRootAttribute("Cars", Namespace = "", IsNullable = false)]
public class Cars
{
[XmlArrayItem(typeof(Car))]
public Car[] Car { get; set; }
public Stream SalesPerson { get; set; }
}
public class Car
{
[System.Xml.Serialization.XmlElementAttribute("StockNumber")]
public string StockNumber{ get; set; }
[System.Xml.Serialization.XmlElementAttribute("Make")]
public string Make{ get; set; }
[System.Xml.Serialization.XmlElementAttribute("Model")]
public string Model{ get; set; }
}
where the SalesPerson property on the Cars object would contain a stream with the raw xml that is within the <SalesPerson> xml element after being run through an XmlSerializer.
Can this be done? Can I choose to only deserialize "part of" an xml document?
Thanks!
-Mike
p.s. example xml stolen from How to Deserialize XML document
It might be a bit old thread, but i will post anyway. i had the same problem (needed to deserialize like 10kb of data from a file that had more than 1MB). In main object (which has a InnerObject that needs to be deserializer) i implemented a IXmlSerializable interface, then changed the ReadXml method.
We have xmlTextReader as input , the first line is to read till a XML tag:
reader.ReadToDescendant("InnerObjectTag"); //tag which matches the InnerObject
Then create XMLSerializer for a type of the object we want to deserialize and deserialize it
XmlSerializer serializer = new XmlSerializer(typeof(InnerObject));
this.innerObject = serializer.Deserialize(reader.ReadSubtree()); //this gives serializer the part of XML that is for the innerObject data
reader.close(); //now skip the rest
this saved me a lot of time to deserialize and allows me to read just a part of XML (just some details that describe the file, which might help the user to decide if the file is what he wants to load).
The accepted answer from user271807 is a great solution but I found, that I also needed to set the xml root of the fragment to avoid an exception with an inner exception saying something like this:
...xmlns=''> was not expected
This exception was trown when I tried to deserialize only the inner Authentication element of this xml document:
<?xml version=""1.0"" encoding=""UTF-8""?>
<Api>
<Authentication>
<sessionid>xxx</sessionid>
<errormessage>xxx</errormessage>
</Authentication>
</ApI>
So I ended up creating this extension method as a reusable solution - warning contains a memory leak, see below:
public static T DeserializeXml<T>(this string #this, string innerStartTag = null)
{
using (var stringReader = new StringReader(#this))
using (var xmlReader = XmlReader.Create(stringReader)) {
if (innerStartTag != null) {
xmlReader.ReadToDescendant(innerStartTag);
var xmlSerializer = new XmlSerializer(typeof(T), new XmlRootAttribute(innerStartTag));
return (T)xmlSerializer.Deserialize(xmlReader.ReadSubtree());
}
return (T)new XmlSerializer(typeof(T)).Deserialize(xmlReader);
}
}
Update 20th March 2017:As the comment below points out, there is a memory leak problem when using one of the constructors of XmlSerializer, so I ended up using a caching solution as shown below:
/// <summary>
/// Deserialize XML string, optionally only an inner fragment of the XML, as specified by the innerStartTag parameter.
/// </summary>
public static T DeserializeXml<T>(this string #this, string innerStartTag = null) {
using (var stringReader = new StringReader(#this)) {
using (var xmlReader = XmlReader.Create(stringReader)) {
if (innerStartTag != null) {
xmlReader.ReadToDescendant(innerStartTag);
var xmlSerializer = CachingXmlSerializerFactory.Create(typeof (T), new XmlRootAttribute(innerStartTag));
return (T) xmlSerializer.Deserialize(xmlReader.ReadSubtree());
}
return (T) CachingXmlSerializerFactory.Create(typeof (T), new XmlRootAttribute("AutochartistAPI")).Deserialize(xmlReader);
}
}
}
/// <summary>
/// A caching factory to avoid memory leaks in the XmlSerializer class.
/// See http://dotnetcodebox.blogspot.dk/2013/01/xmlserializer-class-may-result-in.html
/// </summary>
public static class CachingXmlSerializerFactory {
private static readonly ConcurrentDictionary<string, XmlSerializer> Cache = new ConcurrentDictionary<string, XmlSerializer>();
public static XmlSerializer Create(Type type, XmlRootAttribute root) {
if (type == null) {
throw new ArgumentNullException(nameof(type));
}
if (root == null) {
throw new ArgumentNullException(nameof(root));
}
var key = string.Format(CultureInfo.InvariantCulture, "{0}:{1}", type, root.ElementName);
return Cache.GetOrAdd(key, _ => new XmlSerializer(type, root));
}
public static XmlSerializer Create<T>(XmlRootAttribute root) {
return Create(typeof (T), root);
}
public static XmlSerializer Create<T>() {
return Create(typeof (T));
}
public static XmlSerializer Create<T>(string defaultNamespace) {
return Create(typeof (T), defaultNamespace);
}
public static XmlSerializer Create(Type type) {
return new XmlSerializer(type);
}
public static XmlSerializer Create(Type type, string defaultNamespace) {
return new XmlSerializer(type, defaultNamespace);
}
}
You can control how your serialization is done by implementing the ISerializable interface in your class. Note this will also imply a constructor with the method signature (SerializationInfo info, StreamingContext context) and sure you can do what you are asking with that.
However have a close look at whether or not you really need to do this with streaming because if you don't have to use the streaming mechanism, achieving the same thing with Linq to XML will be easier, and, simpler to maintain in the long term (IMO)
I think the previous commenter is correct in his comment that XML might not be the best choice of a backing store here.
If you are having issues of scale and aren't taking advantage of some of the other niceties you get with XML, like transforms, you might be better off using a database for your data. The operations you are doing really seem to fit more into that model.
I know this doesn't really answer your question, but I thought I would highlight an alternate solution you might use. A good database and an appropriate OR mapper like .netTiers, NHibernate, or more recently LINQ to SQL / Entity Framework would probably get you back up and running with minimal changes to the rest of your codebase.
Typically XML deserialization is an all-or-nothing proposition out of the box, so you'll probably need to customize. If you don't do a full deserialization, you run the risk that the xml is malformed within the SalesPerson element, and so the document is invalid.
If you are willing to accept that risk, you'll probably want to do some basic text parsing to break out the SalesPerson elements into a different document using plain text processing facilities, then process the XML.
This is a good example of why XML is not always the correct answer.
Please try defining the SalesPerson property as type XmlElement. This works for output from ASMX web services, which use XML Serialization. I would think it would work on input as well. I would expect the entire <SalesPerson> element to wind up in the XmlElement.
You may control what parts of the Cars class are deserialized by implementing the IXmlSerializable interface on the Cars class, and then within the ReadXml(XmlReader) method you would read and deserialize the Car elements but when you reach the SalesPerson element you would read its subtree as a string and then construct a Stream over the the textual content using a StreamWriter.
If you never want the XmlSerializer to write out the SalesPerson element, use the [XmlIgnore] attribute. I am not sure what you want to happen when you seriailize the Cars class to its XML representation. Are you trying to only prevent deserialization of the SalesPerson while still being able to serialize the XML representation of the SalesPerson represented by the Stream?
I could probably provide a code example of this if you want a concrete implementation.
If all you want to do is parse out the SalesPerson element but keep it as a string, you should use Xsl Transform rather than "Deserialization". If, on the other hand, you want to parse out the SalesPerson element and only populate an object in memory from all the other non-SalesPerson elements, then Xsl Transform might also be the way to go. If the files are way big, you may consider separating them and using Xsl to combine different xml files so that the SalesPerson I/O only occurs when you need it to.
I would suggest you to manually read from Xml, using any lightweight methods, like XmlReader, XPathDocument or LINQ-to-XML.
When you have to read only 3 properties, I suppose you can write code that manually read from that node and have a full control of how it is executed instead of relying on Serialization/Deserialization
What I have is a collection of classes that all implement the same interface but can be pretty wildly different under the hood. I want to have a config file control which of the classes go into the collection upon starting the program, taking something that looks like :
<class1 prop1="foo" prop2="bar"/>
and turning that into :
blah = new class1();
blah.prop1="foo";
blah.prop2="bar";
In a very generic way. The thing I don't know how to do is take the string prop1 in the config file and turn that into the actual property accessor in the code. Are there any meta-programming facilities in C# to allow that?
Reflection allows you to do that. You also may want to look at XML Serialization.
Type type = blah.GetType();
PropertyInfo prop = type.GetProperty("prop1");
prop.SetValue(blah, "foo", null);
It may be easier to serialise the classes to/from xml, you can then simply pass the XmlReader (which is reading your config file) to the deserializer and it will do the rest for you..
This is a pretty good article on serialization
Edit
One thing I would like to add, even though reflection is powerful, it requires you to know some stuff about the type, such as parameters etc.
Serializing to XML doesnt need any of that, and you can still have type safety by ensuring you write the fully qualified type name to the XML file, so the same type is automatically loaded.
I would also suggest Xml serialization as others have already mentioned. Here is a sample I threw together to demonstrate. Attributes are used to connect the names from the Xml to the actual property names and types in the data structure. Attributes also list out all the allowed types that can go into the Things collection. Everything in this collection must have a common base class. You said you have a common interface already -- but you may have to change that to an abstract base class because this code sample did not immediately work when Thing was an interface.
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main()
{
string xml =
"<?xml version=\"1.0\"?>" +
"<config>" +
"<stuff>" +
" <class1 prop1=\"foo\" prop2=\"bar\"></class1>" +
" <class2 prop1=\"FOO\" prop2=\"BAR\" prop3=\"42\"></class2>" +
"</stuff>" +
"</config>";
StringReader sr = new StringReader(xml);
XmlSerializer xs = new XmlSerializer(typeof(ThingCollection));
ThingCollection tc = (ThingCollection)xs.Deserialize(sr);
foreach (Thing t in tc.Things)
{
Console.WriteLine(t.ToString());
}
}
}
public abstract class Thing
{
}
[XmlType(TypeName="class1")]
public class SomeThing : Thing
{
private string pn1;
private string pn2;
public SomeThing()
{
}
[XmlAttribute("prop1")]
public string PropertyNumber1
{
get { return pn1; }
set { pn1 = value; }
}
[XmlAttribute("prop2")]
public string AnotherProperty
{
get { return pn2; }
set { pn2 = value; }
}
}
[XmlType(TypeName="class2")]
public class SomeThingElse : SomeThing
{
private int answer;
public SomeThingElse()
{
}
[XmlAttribute("prop3")]
public int TheAnswer
{
get { return answer; }
set { answer =value; }
}
}
[XmlType(TypeName = "config")]
public class ThingCollection
{
private List<Thing> things;
public ThingCollection()
{
Things = new List<Thing>();
}
[XmlArray("stuff")]
[XmlArrayItem(typeof(SomeThing))]
[XmlArrayItem(typeof(SomeThingElse))]
public List<Thing> Things
{
get { return things; }
set { things = value; }
}
}
}
Reflection or XML-serialization is what you're looking for.
Using reflection you could look up the type using something like this
public IYourInterface GetClass(string className)
{
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (Type type in asm.GetTypes())
{
if (type.Name == className)
return Activator.CreateInstance(type) as IYourInterface;
}
}
return null;
}
Note that this will go through all assemblies. You might want to reduce it to only include the currently executing assembly.
For assigning property values you also use reflection. Something along the lines of
IYourInterface o = GetClass("class1");
o.GetType().GetProperty("prop1").SetValue(o, "foo", null);
While reflection might be the most flexible solution you should also take a look at XML-serialization in order to skip doing the heavy lifting yourself.
Plenty of metaprogramming facilities.
Specifically, you can get a reference to the assembly that holds these classes, then easily get the Type of a class from its name. See Assembly.GetType Method (String).
From there, you can instantiate the class using Activator or the constructor of the Type itself. See Activator.CreateInstance Method.
Once you have an instance, you can set properties by again using the Type object. See Type.GetProperty Method and/or Type.GetField Method along PropertyInfo.SetValue Method.
I recently did something very similar, I used an abstract factory. In fact, you can see the basic concept here:
Abstract Factory Design Pattern
I think you can utilize Dynamics here. Create ExpandoObject, it can be used either as Dictionary for setting properties from xml config.
Reflection is what you want. Reflection + TypeConverter. Don't have much more time to explain, but just google those, and you should be well on your way. Or you could just use the xml serializer, but then you have to adhere to a format, but works great.