Suppose I have class CarResource,
class RaceCarResource : public CarResource,
and class SuperDuperUltraRaceCarResource : public RaceCarResource.
I want to be able to load their data using a single method LoadFromXML.
How would I go about getting the CarResource:LoadFromXML to load it's data,
RaceCarResource to call CarResource:LoadFromXML and then load it's own additional data, etc. ?
If I use XmlTextReader I only know how to parse the entire file in one go,
not how to use it so first CarResource:LoadFromXML can do its thing, then RaceCarResource, etc.
I hope it's at least a little bit clear what I mean :)
public class CarResource
{
public virtual void LoadFromXML(String xmlData)
{
...
}
}
public class RaceCarResource : CarResource
{
public override void LoadFromXML(String xmlData)
{
base.LoadFromXML(xmlData);
...
}
}
...and so on. The new keyword will hide the inheritted method but still allow it to be call-able from the child class.
As for actually parsing the XML, you have a couple of options. My first suggestion would be to read the entire XML file in to memory...and then use LINQ to XML to parse through and populate your classes. You could also try the XmlSerializer (LINQ to XML is easier to implement, but as the size of your code-base grows, Xml Serialization can make maintenance easier).
You could also use XML Serialization depending on the structure of your XML file to load from. It's possible to override the load method (and then override in subsequent classes) to load specific information - or just use attributes. See: http://msdn.microsoft.com/en-us/library/ms950721.aspx
You have a couple of options.
You can use Linq to XML to query the child entities and pass those nodes to your other classes. This is probably the most efficient way of doing it.
You could use an xmlnavigator, again only passing the appropriate child nodes...
see: Implementing my own XPathNavigator in C#
You could simply use xml serialization (XmlSerialize XmlDeserialize), see C# - How to xml deserialize object itself?
In order to use XML de-serialization, the instance method makes the current object effectively 'immutable', but I would suggest something like this:
public class CarResource
{
public CarResource LoadNewFromXML(string xml)
{
XmlSerializer ser = new XmlSerializer(this.GetType());
object o = null;
using (MemoryStream ms = new MemoryStream(Encoding.ASCII.GetBytes(xml)))
{
o = ser.Deserialize(ms);
}
return o as CarResource;
}
}
public class RaceCarResource : CarResource
{
}
public class SuperRaceCarResource : RaceCarResource
{
}
Your calling code then looks like:
RaceCarResource car = new RaceCarResource();
car = car.LoadNewFromXML("<RaceCarResource/>") as RaceCarResource;
SuperRaceCarResource sc = new SuperRaceCarResource();
sc = sc.LoadNewFromXML("<SuperRaceCarResource/>") as SuperRaceCarResource;
If your XML is not compatible with the .net XML serialisation, then the easiest way is to create a factory which detects which type of resource the XML represents, then handles that appropriately. If you want to put the parsing into your objects, then use a virtual method to parse the internals after creating the object:
class CarResource
{
public string Color { get; private set; }
internal virtual void ReadFrom(XmlReader xml)
{
this.Color = xml.GetAttribute("colour");
}
}
class RaceCarResource : CarResource
{
public string Sponsor { get; private set; }
internal override void ReadFrom(XmlReader xml)
{
base.ReadFrom(xml);
this.Sponsor = xml.GetAttribute("name-on-adverts");
}
}
class SuperDuperUltraRaceCarResource : RaceCarResource
{
public string Super { get; private set; }
internal override void ReadFrom(XmlReader xml)
{
base.ReadFrom(xml);
this.Super = xml.GetAttribute("soup");
}
}
class CarResourceFactory
{
public CarResource Read(XmlReader xml)
{
CarResource car;
switch (xml.LocalName)
{
case "ordinary-car": car = new CarResource(); break;
case "racecar": car = new RaceCarResource(); break;
case "super_duper": car = new SuperDuperUltraRaceCarResource(); break;
default: throw new XmlException();
}
XmlReader sub = xml.ReadSubtree();
car.ReadFrom(sub);
sub.Close();
return car;
}
}
This works OK if the XML for a sub-type has any child elements appended strictly after or before the content for the super-type. Otherwise you have to do more work to reuse the super-type's serialisation, breaking it up into smaller methods (eg the base has methods to load the number of wheels, doors, engine size; the race car calls LoadDoorData, LoadAeroFoilData, LoadWheelData if the XML for the race car has the aerofoil data in between the door and wheel data. For formats with no logical ordering imposed (XMI, RDF) you have to inspect the local name to decide which specialised method to call, which gets a bit messy if you want to combine it with virtual methods. In that case, it's better to use a separate serialisation helper.
Other mechanisms can be used in the factory if the set of types to be created is not fixed to a few types.
Related
What is the best approach when I do not know at runtime which types are de/serialized with protobuf?
Currently I am playing with the idea to extend the RuntimeTypeModel in the type initializers of the types which are candidates for serialization which seems to work pretty well for serialiation. But when deserializing in a different process I would need to load the same type model from somewhere which was used to serialize the types. Is it possible to serialize the RuntimeTypeModel to disk to reuse it later when the serialized data is read again from disk? Ideally I would put the model into the serialized stream as well to have a full self describing object model. Or would I need to record the steps and put this data in front of my serialized stream?
One could create a header which contains the offset to the real data and the runtime type model and the length which would be pretty nice. Or is there a better approach how to deal with a plug in architecture where at serialization time I have all types registered but during deseralization I might stil need to load some types from their respective assemblies because the code was not yet touched?
using ProtoBuf;
using ProtoBuf.Meta;
using System.Collections.Generic;
using System.IO;
namespace protobuf
{
[ProtoContract]
public interface IAbstraction
{
[ProtoMember(1)]
string Name { get; set; }
}
[ProtoContract]
public class Base : IAbstraction
{
static Base()
{
ProtobufTypeModels.MainModel.Add(typeof(IAbstraction), true).AddSubType(101, typeof(Base));
}
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2, AsReference =true)]
public List<IAbstraction> Instances = new List<IAbstraction>();
}
[ProtoContract]
public class Next : Base
{
static Next()
{
ProtobufTypeModels.MainModel.Add(typeof(IAbstraction), true).AddSubType(100, typeof(Next));
}
[ProtoMember(1)]
public string NextName { get; set; }
}
public static class ProtobufTypeModels
{
public static readonly RuntimeTypeModel MainModel = TypeModel.Create();
}
class Program
{
static void Main(string[] args)
{
Base b = new Base { Name = "Alois" };
b.Instances.Add(new Next { Name = "Base", NextName = "Christian" });
b.Instances.Add(new Base { Name = "SecondBase", Instances = b.Instances });
var mem = new MemoryStream();
ProtobufTypeModels.MainModel.Serialize(mem, b);
mem.Position = 0;
var deser = (Base) ProtobufTypeModels.MainModel.Deserialize(mem, null, typeof(Base));
}
}
}
Random thought: you could use .Compile(serializerName,dllPath) after you've finished the initialization and write the baked serializer to disk; then you can reference it, use new SerializerName() to create the instance, and use the .Serialize etc methods from there. The dll will never change. This also means it never has to process any metadata ever again; no reflection, no IL emit, etc.
Other than that: we could possibly do something more gentle in terms of storing the configuration, but: protobuf-net doesn't currently add anything directly to support it, and it would probably be more relevant for you to have your own bespoke configuration data that you simply consume at startup.
I am writing an application that allows a user to run a test. A test consists of a number of different objects, such as configuration, temperature, and benchmark. Settings and the like are saved back and forth between xml. I pass different XElements around in my code so I can build the final xml document differently for different situations. I wish to do something like this:
public abstract class BaseClass<T>
{
abstract static XElement Save(List<T>);
abstract static List<T> Load(XElement structure);
}
public class Configuration : BaseClass<Configuration>
{
public string Property1 { get; set; }
public string Property2 { get; set; }
//etc...
public static XElement Save(List<Configuration>)
{
XElement xRoot = new XElement("Root");
//etc...
return xRoot;
}
public static List<Configuration> Load(XElement structure)
{
List<BaseClass> list = new List<BaseClass>();
//etc...
return list;
}
}
public class Temperature : BaseClass<Temperature>
{
public float Value { get; set; }
public static XElement Save(List<Temperature>)
{
//save
}
public static List<Temperature> Load(XElement structure)
{
//load
}
}
[EDIT]: Revising question (Changed signatures of above functions)[/EDIT]
Of course, I am not actually allowed to override the static methods of BaseClass. What is the best way to approach this? I would like as much of the following to be valid as possible:
List<Temperature> mTemps = Temperature.Load(element);
List<Configuration> mConfigs = Configuration.Load(element);
Temperature.Save(mTemps);
Configuration.Save(mConfigs);
[EDIT]Changed intended usage code above[/EDIT]
The only solution I can think of is the following, which is NOT acceptable:
public class File
{
public static XElement Save(List<Temperature> temps)
{
//save temp.Value
}
public static XElement Save(List<Configuration> configs)
{
//save config.Property1
//save config.Property2
}
//etc...
}
Static methods aren't part of a class instance. So overriding them doesn't make any sense anyway. They can't access any nonstatic part of an instance that they happen to be a member of.
This is kind of a strategy pattern scenario, e.g. you could just have single static Load & Save methods that check the type of object passed to them, and act accordingly. But here's another slightly more clever way that uses generic types to create a prototype and call its method, allowing you to keep the logic within each derived object type.
(edit again)
Here's another crack at it, along the same lines as my original suggestion. I actually tested this and it works, so I think this is the best you can do to get all the functionality you are looking for (other than testing types and calling code conditionally). You still need to pass a type for Load, otherwise, the runtime would have no idea what kind of return is expected. But Save works universally. And the subclass implementations are strongly typed.
This just uses the first object in the list as its prototype, simple enough.
public interface IBaseObject
{
XmlElement Save(IEnumerable<IBaseObject> list);
IEnumerable<IBaseObject> Load(XmlElement element);
}
public interface IBaseObject<T> where T: IBaseObject
{
XmlElement Save(IEnumerable<T> list);
IEnumerable<T> Load(XmlElement element);
}
public class Temperature : IBaseObject<Temperature>, IBaseObject
{
public XmlElement Save(IEnumerable<Temperature> list)
{
throw new NotImplementedException("Save in Temperature was called");
}
public IEnumerable<Temperature> Load(XmlElement element)
{
throw new NotImplementedException("Load in Temperature was called");
}
// must implement the nongeneric interface explicitly as well
XmlElement IBaseObject.Save(IEnumerable<IBaseObject> list)
{
return Save((IEnumerable<Temperature>)list);
}
IEnumerable<IBaseObject> IBaseObject.Load(XmlElement element)
{
return Load(element);
}
}
// or whatever class you want your static methods living in
public class BaseObjectFile
{
public static XmlElement Save(IEnumerable<IBaseObject> list)
{
IBaseObject obj = list.DefaultIfEmpty(null).First(); // linq
return obj==null ? null : obj.Save(list);
}
public static IEnumerable<IBaseObject> Load<T>(XmlElement element)
where T: IBaseObject, new()
{
IBaseObject proto = new T();
return proto.Load(element);
}
}
(original edit)
This has a problem in that you must call the static methods with a type, e.g.
BaseClass<Temperature>.Load()
There is a way around this for the Save method, but part of what you want is not possible. The Load method cannot know what type of list to return because its only parameter has no information about the return type. Hence, it can't possibly decide which type to create as a prototype. So no matter what, if you wanted to use common Load method, you would have to pass it a type like the above syntax.
For the Save method, you could use reflection to create the prototype in the static method, by obtaining the type from the first element, and then call the Save method from the prototype. So if you only need the Save method to be used as you like, that much is possible.
Ultimately, though, I think it would be a lot simpler to do something like this:
public static XElement Save(List<IBaseClass> list)
{
if (list is Temperature) {
// do temperature code
} else if (list is SomethingElse) {
// do something else
}
}
Anyway - like I said it's going to require reflection to make even the Save method work in this way. I'd just use the simple approach.
(original bad code removed)
If you don't really care about the format in which its saved, you're free to use serialisation (which uses reflection internally).
string SerialiseToString<T>(T source)
{
using (StringWriter sw = new StringWriter() && XmlSerializer xml = new XmlSerializer(typeof(OrderedItem)))
{
xml.Serializer(sw, source);
return sw.ToString();
}
}
If you want to incorporate it into a larger part of your XML file, the easiest way would be to parse this output and add it to yours. Alternatively, you could reflect the properties yourself.
If the shared part is the same, you can put it in BaseClass:
public static XElement Save(IEnumerable<BaseClass> list)
{
var root = new XElement("root");
foreach (var item in list)
{
item.Save(root);
}
return root;
}
Here, Save(XElement) is a virtual method, each type implements it.
Obviously, you can't do this with loading, you either have to know what type are you loading, or have some way of finding out which type are you loading.
Here is a simplified version of what I'm trying to do:
Without having multiple if..else clauses and switch blocks, can I mimic the behavior of Javascript's eval() shudder to instantiate a class in C#?
// Determine report orientation -- Portrait or Landscape
// There are 2 differently styled reports (beyond paper orientation)
string reportType = "Portrait";
GenericReport report;
report = new eval(reportType + "Report()"); // Resolves to PortraitReport()
The need stems from the fact that I have 6 types of Crystal Reports (that do the same thing, but look drastically different) for 50 states. There are 3 styles each, rather than entertain the notion of a giant switch block with nested if..else statements determining which of 900 reports to use, I was hoping for an eval-like solution.
You could use Activator.CreateInstance("myAssembly", "PortrainReport");. Although the more readable way would be to create a Portrait Factory, which would create the correct type for you.
As people specified above, you can use Activator class to create an instance of the class by its text name.
But, there is one more option.
When you told about using eval like function in c# i assumed, you not only want to create an instance of the class by its text name, but also fill it with properties from the same string.
For this purpose you need to use deserialization.
Deserialization converts string like representation of the class into its instance and restoring all its properties that was specified in the string.
Xml serialization. Its using XML file for converting into instance.
Here is small example:
public class Report1
{
public string Orientation {get;set;}
public string ReportParameter1 {get;set;}
public string ReportParameter2 {get;set;}
}
Above is the class that you want to instantiate and fill with parameters by string line.
Below is XML that can do that:
<?xml version="1.0"?>
<Report1>
<Orientation>Landscape</Orientation>
<ReportParameter1>Page1</ReportParameter1>
<ReportParameter2>Colorado</ReportParameter2>
</Report1>
To create an instance from the file use System.Xml.Serialization.XmlSerializer :
string xml = #"<?xml version=""1.0""?>
<Report1>
<Orientation>Landscape</Orientation>
<ReportParameter1>Page1</ReportParameter1>
<ReportParameter2>Colorado</ReportParameter2>
</Report1>";
///Create stream for serializer and put there our xml
MemoryStream str = new MemoryStream(ASCIIEncoding.ASCII.GetBytes(xml));
///Getting type that we are expecting. We are doing it by passing proper namespace and class name string
Type expectingType = Assembly.GetExecutingAssembly().GetType("ConsoleApplication1.Report1");
XmlSerializer ser = new XmlSerializer(expectingType);
///Deserializing the xml into the object
object obj = ser.Deserialize(str);
///Now we have our report instance initialized
Report1 report = obj as Report1;
In this way you can prepare appropriate xml as string concatenation. That xml will contain all parameters for your report.
Then, you can convert it into the proper type.
Look at the Activator create instance method
All the classes will need to adhere to an interface. Then make an Generic Method which will be your eval and requires that interface. Here is an example of this (call the Usage static to see it in action):
public interface IOperation
{
string OutputDirection { get; set; }
};
public class MyOperation: IOperation
{
public string OutputDirection { get; set; }
}
public static class EvalExample
{
public static T Eval<T>( string direction ) where T : IOperation
{
T target = (T) Activator.CreateInstance( typeof( T ) );
target.OutputDirection = direction;
return target;
}
// Example only
public static void Usage()
{
MyOperation mv = Eval<MyOperation>( "Horizontal" );
Console.WriteLine( mv.OutputDirection ); // Horizontal
}
}
Using the factory pattern, and reflection (as explained in this blog post), you would get:
static void Main(string[] args)
{
ReportFactory<Report> factory = new ReportFactory<Report>();
Report r1 = factory.CreateObject("LandscapeReport");
Report r2 = factory.CreateObject("PortraitReport");
Console.WriteLine(r1.WhoAmI());
Console.WriteLine(r2.WhoAmI());
}
Which would output "Landscape" and "Portrait", respectivley.
Of course, for the plumbing, you need an interface that all your reports are based off of (which I assume you already have).
For this example:
public interface Report
{
string WhoAmI();
}
And the two implemenations:
public class PortraitReport : Report
{
public string WhoAmI()
{
return "Portrait";
}
}
public class LandscapeReport : Report
{
public string WhoAmI()
{
return "Landscape";
}
}
The secret is in the ReportFactory, which uses Reflection to see what other classes are based on Report, and automatically register them for use, which I think is pretty cool:
public class ReportFactory<Report>
{
private Dictionary<string, Type> reportMap = new Dictionary<string, Type>();
public ReportFactory()
{
Type[] reportTypes = Assembly.GetAssembly(typeof(Report)).GetTypes();
foreach (Type reportType in reportTypes)
{
if (!typeof(Report).IsAssignableFrom(reportType) || reportType == typeof(Report))
{
// reportType is not derived from Report
continue;
}
reportMap.Add(reportType.Name, reportType);
}
}
public Report CreateObject(string ReportName, params object[] args)
{
return (Report)Activator.CreateInstance(reportMap[ReportName], args);
}
}
So now all you have to do is just add any new implementations of Report in your assembly, and they will be available to the factory with no extra coding or changing other code files.
I am using C# + VSTS2008 + .Net 3.0 to do XML serialization. The code works fine. Here below is my code and current serialized XML results.
Now I want to add two additional layers to the output XML file. Here is my expected XML results. Any easy way to do this? I am not sure whether NestingLevel could help to do this. I want to find an easy way which does not change the structure of MyClass and MyObject.
Expected XML serialization result,
<?xml version="1.0"?>
<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyObjectProperty>
<AdditionalLayer1>
<AdditionalLayer2>
<ObjectName>Foo</ObjectName>
</AdditionalLayer1>
</AdditionalLayer2>
</MyObjectProperty>
</MyClass>
Current XML serialization result,
<?xml version="1.0"?>
<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyObjectProperty>
<ObjectName>Foo</ObjectName>
</MyObjectProperty>
</MyClass>
My current code,
public class MyClass
{
public MyObject MyObjectProperty;
}
public class MyObject
{
public string ObjectName;
}
public class Program
{
static void Main(string[] args)
{
XmlSerializer s = new XmlSerializer(typeof(MyClass));
FileStream fs = new FileStream("foo.xml", FileMode.Create);
MyClass instance = new MyClass();
instance.MyObjectProperty = new MyObject();
instance.MyObjectProperty.ObjectName = "Foo";
s.Serialize(fs, instance);
return;
}
}
I want to find an easy way which does not change the structure of MyClass and MyObject.
Frankly, that isn't going to happen. XmlSerializer (when used simply) follows the structure of the classes / members. So you can't add extra layers without changing the structure.
When used in a bespoke way (IXmlSerializable), the one thing you can say for sure is that it isn't simple... this is not a fun interface to implement.
My advice: introduce a DTO that mimics your desired format, and shim to that before you serialize.
Why do you need these additional two layers?
Is its a business requirement, you should add these two objects to your object model:
public class MyClass
{
public AdditionalLayer1 AdditionalLayer1;
}
public class AdditionalLayer1
{
public AdditionalLayer2 AdditionalLayer2;
}
public class AdditionalLayer2
{
public MyObject MyObjectProperty;
}
public class MyObject
{
public string ObjectName;
}
if its purely because of some compatibility requirements you can either do the thing above, or implement IXmlSerializable on MyClass:
public class MyClass : IXmlSerializable
{
public MyObject MyObjectProperty;
public void WriteXml (XmlWriter writer)
{
//open the two layers
//serialize MyObject
//close the two layers
}
public void ReadXml (XmlReader reader)
{
//read all the layers back, etc
}
public XmlSchema GetSchema()
{
return(null);
}
}
You could do a transformation with XSL after serializing and add the "missing" tags.
I would suggest serializing out to a MemoryStream instead of a FileStream and then loading the XML into an XmlDocument, then using the methods of that class to insert the extra nodes you want. After that, you can save it out.
I did exactly this only recently and didn't find overriding the IXmlSerializable interface at all difficult or complicated. Serialize/deserialize as normal but for the class that contains the sub-class which you need to wrap in extra tags derive from IXmlSerializable:
class ContainingClass : IXmlSerializable
{
...
#region IXmlSerializable Members
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
**reader.ReadStartElement("Equipment");**
XmlSerializer ser = new XmlSerializer(typeof(Host));
while (reader.NodeType != XmlNodeType.EndElement)
{
Host newHost = new Host();
newHost.Name = (string) ser.Deserialize(reader);
_hosts.Add(newHost);
reader.Read();
}
// Read the node to its end.
// Next call of the reader methods will crash if not called.
**reader.ReadEndElement(); // "Equipment"**
}
public void WriteXml(XmlWriter writer)
{
XmlSerializer ser = new XmlSerializer(typeof(Host));
**writer.WriteStartElement("Equipment");**
foreach (Host host in this._hosts)
{
ser.Serialize(writer, host);
}
**writer.WriteEndElement();**
}
#endregion
All I do here is to wrap the de/serialization of the lower level objects. Here I'm wrapping an extra tag "Equipment" around all the Hosts in a collection in this class. You could add as many tags as you like here as long as you close each one you start.
NOTE: Bold is showing as ** text ** - just make sure you get rid of the double asterisks :)
I'm building a game in XNA 3 and I have the levels stored in XML format. What solution would you recommend for deserializing those XML files into level objects? it needs to be able to work on the Xbox.
I haven't tried it on the 360 (I bet it will work), but XmlSerializer is a great simple way to save/load your object graphs into XML. Basically, you take your xml file and run xsd.exe against it. That will generate a set of C# classes that you can deserialize your XML into. In your code you will write something like:
var serializer = new XmlSerializer(typeof(LevelType));
var level = (LevelType)serializer.Deserialize(streamToYourLevel);
All done.
I wouldn't recommend (de)serializing the actual levels, just create a simple container class that contains the information to construct a level object from it.
something like:
[Serializable()]
public class LevelDefinition
{
public Vector3 PlayerStartPosition { get; set; }
public string LevelName { get; set; }
public List<Enemy> Enemies { get; set; }
... etc
}
This will result in nice, clean XML.
And then just use the XmlSerializer class to deserialize it.
I don't think binary serialization is available for Zune and XBox but XmlSerializer works fine for me. I have no problems serializing collections but you have to use XmlArrayItem attribute for untyped collections like ArrayList or pass additonal type information to the XmlSerializer constructor but it is better and simpler to use List nowadays. Dictionary cannot be serialized but you can create a wrapper class for that. I usually store a unique ID for each item which can then be used as the key in the dictionary.
Then I can create a class that wraps a Dictionary but exposed as a collection of items.
public class MyItem {
public string ID { get; set; }
:
}
public class MyList : ICollection<MyItem> {
private Dictionary<string,MyItem> items;
public MyList() {
items = new Dictionary<string, MyItem>();
}
void Add(MyItem item) {
items.Add(item.ID, item);
}
:
}