I want to create a method that serializes lists of different childs. Each child gets his own XML file. Doing this for a single object was easy:
interface ISerializable
{
}
public class Item : ISerializable
{
public string name { get; set; }
public int number { get; set; }
}
public class Weapon : ISerializable
{
public string name { get; set; }
public int damage {get; set;}
}
public static void SerializeToXML(ISerializable child)
{
XmlSerializer serializer = new XmlSerializer(child.GetType());
using (TextWriter textWriter = new StreamWriter("test.xml"))
{
serializer.Serialize(textWriter, child);
}
}
I can put anything derived from ISerializable into the serialize method to get a desired result. However when give List<ISerializable> as possible argument it does not compile Cannot convert List<item> to List<ISerializable>. Anyway to solve this?
public static void SerializeListToXML(List<ISerializable> listToXML)
{
XmlSerializer serializer = new XmlSerializer(listToXML.GetType());
using (TextWriter textWriter = new StreamWriter("test.xml"))
{
serializer.Serialize(textWriter, listToXML);
}
}
The base reason i'm doing this is to get XML templates of much larger structures so i an manually add XML to load into my program. I figured i would benefit from creating a serializer first so i have the correct XML structure to start with. Perhaps this can come in handy later down the line for saving user data too.
SerializeListToXML should take an IEnumerable<ISerializable> instead of a List<Item>.
A List<Item> cannot be casted to List<ISerializable>. If you could to that, then you'd be able to add any ISerializable to the list, not just Items. With an IEnumerable<>, you cannot modify the collection - only iterate over it. Since SerializeListToXML does not need to modify the collection, it can accept an IEnumerable<ISerializable>, which is less restrictive. Please see the following section and sub-sections about covariance and contravariance: Covariance and Contravariance
Related
Given the following XML I want to deserialize:
<?xml version="1.0" encoding="utf-8" ?>
<units>
<entity>
<health max="1000"/>
<sprite texture="tank"/>
<entity>
<sprite texture="tank-turret"/> <!-- this element is missing when i deserialize --!>
</entity>
</entity>
</units>
How can I deserialize this recursive object graph using XmlSerializer?
The following is my last try. It successfully deserializes the top-level objects (health, sprite, entity) but it does not seem to find the sprite element in the nested entity node.
I also tried deriving entity from componentlist, but it didn't work either.
public class UnitSerializer
{
public abstract class item
{
}
public class entity : item
{
[XmlArray("entity")]
[XmlArrayItem(typeof(health))]
[XmlArrayItem(typeof(entity))]
[XmlArrayItem(typeof(sprite))]
public componentlist entity2 { get; set; }
}
public abstract class component : item
{
}
public class health : component
{
[XmlAttribute]
public int max { get; set; }
}
public class sprite : component
{
[XmlAttribute]
public string texture { get; set; }
}
public class componentlist : List<item>
{
}
[XmlRoot("units")]
public class units
{
[XmlArray("entity")]
[XmlArrayItem(typeof(health))]
[XmlArrayItem(typeof(entity))]
[XmlArrayItem(typeof(sprite))]
public componentlist entity { get; set; }
}
public void Read()
{
var x = new XmlSerializer(typeof(units),
new[] {
typeof(componentlist),
typeof(entity),
typeof(health),
typeof(sprite)
});
var fs = new FileStream("units.xml", FileMode.Open);
XmlReader reader = new XmlTextReader(fs);
var units = (units)x.Deserialize(reader);
}
}
Your classes can be fixed by replacing use of [XmlArray] and [XmlArrayItem] with [XmlElement(typeof(TDerived))]:
public class UnitSerializer
{
public abstract class item
{
}
public class entity : item
{
[XmlElement("health", typeof(health))]
[XmlElement("entity", typeof(entity))]
[XmlElement("sprite", typeof(sprite))]
public List<item> EntityList { get; set; }
}
public abstract class component : item
{
}
public class health : component
{
[XmlAttribute]
public int max { get; set; }
}
public class sprite : component
{
[XmlAttribute]
public string texture { get; set; }
}
[XmlRoot("units")]
public class units
{
[XmlElement("health", typeof(health))]
[XmlElement("entity", typeof(entity))]
[XmlElement("sprite", typeof(sprite))]
public List<item> EntityList { get; set; }
}
public units Read(string filename)
{
var x = new XmlSerializer(typeof(units));
using (var fs = new FileStream(filename, FileMode.Open))
using (var reader = XmlReader.Create(fs))
{
return (units)x.Deserialize(reader);
}
}
}
Notes:
[XmlArray] indicates a collection should be serialized with an outer wrapper element containing a sequence of elements, while [XmlElement] indicates a collection should be serialized as a sequence without the wrapper. Your XML sample uses repeating elements without wrapper elements, so [XmlElement] should be used. It sort of works because your XML is recursive -- but at every other level repeating elements are getting incorrectly deserialized as wrappers. This explains why some but not all data is lost during deserialization.
In your XML sample, polymorphic elements are identified by element name. The XmlSerializer(Type, Type[]) constructor should be used to specify polymorphic included types to be serialized using the xsi:type mechanism. Since the xsi:type attribute does not appear in your XML, this constructor need not be used.
(In addition, when constructing an XmlSerializer using the XmlSerializer(Type, Type[]) constructor, you must cache the serializer statically to avoid a severe memory leak. See Memory Leak using StreamReader and XmlSerializer for why.)
XmlTextReader has been deprecated since .Net 2.0. Use XmlReader.Create() instead.
The FileStream and XmlReader should be disposed, ideally via using statements.
I eliminated the public class componentlist : List<item> and replaced it with just a List<item>. This was mainly a matter of taste, but it does make it easier to set the value of such a list using Linq's .ToList().
Demo fiddle here.
for our software I've to change the xml serialization for on entiy of our project.
After a couple of hours I've found, that you can modify the xml serialization if you implement the interface IXmlSerializable.
After some reading about how to implement this interface correctly, I've implemented this into the entity.
The entity which is implementing this interface is a child class. The parent class will be serialized into xml. If the child class is just a normal property, the serialization is correct and works like a charm.
But if the parent class has a list of this child classes, the serialization ends into an endless loop.
Here's a very simple example to illustrate my problem:
public class ParentEntity
{
public List<ChildEntity> Childs { get; set; } = new List<ChildEntity>();
}
public class ChildEntity: IXmlSerializable
{
public string Name { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
Name = reader.GetAttribute("Name");
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("Name", Name);
}
}
And this is the logik I'm using for the serialization
using (var memoryStream = new MemoryStream())
{
var serializer = new XmlSerializer(typeof(ParentEntity));
serializer.Serialize(memoryStream, parentEntity);
memoryStream.Position = 0;
var deserializedParentEntity = serializer.Deserialize(memoryStream) as ParentEntity;
Console.Read();
}
The object parentEntity has been initialized correctly.
Then I'm serializing the object into xml.
After this, I'm deserializing the xml back into an object (Just for the showcase).
And here is my problem. The method ReadXml() get called very very often. It looks like a endless loop. The end of the deserialization process I've never reached.
Has anyone an idea what my problem is?
As explained in Proper way to implement IXmlSerializable, the method ReadXml() is responsible for reading the entire element including the <ChildEntity> start and end nodes, while WriteXml() should not do this as the wrapper element is written by the framework itself. If ReadXml() doesn't advance the reader past the initial element, you will get the infinite loop you are seeing.
Thus your ChildEntity type should look like:
public class ChildEntity : IXmlSerializable
{
public string Name { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
Name = reader.GetAttribute("Name");
reader.Skip(); // Consume the <ChildEntity> and </ChildEntity> (if present) nodes and everything contained therein.
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("Name", Name);
}
}
Sample fiddle.
I have a class that I need to serialize/deserialize, and I'm half way there - I have serialization functional, resulting in the below XML. However, since I'm implementing IXmlSerializable myself, I'm uncertain what an implementation of ReadXml should look like, given that SomeGenericClass<T> was serialized using attribute-based flagging rather than an explicit implementation if IXmlSerializable
<?xml version="1.0" encoding="utf-16"?>
<FooContainer FooName="DoSomething">
<SomeGenericClassOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Value="Foobar" Name="firstParam" Description="First Paramater Serialized" />
<SomeGenericClassOfInt32 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Value="10000" Name="nextParam" Description="Second Serialized parameter" />
</FooContainer>
Which I want to serialize back into an instance of:
public class FooContainer : IList<ISomeGenericClassBase>, IXmlSerializable
{
public string FooName {get;set;}
void IXmlSerializable.WriteXml(XmlWriter writer) {
var serializer = XmlSerializer.FromTypes(new Type[]{SomeGenericBaseClass})[0];
this
.Select(item=>SomeGenericClassBase.ConvertToMe(item))
.ToList()
.ForEach(item=>serializer.Serialize(writer, item));
}
// IList Implementation omitted - wraps a private List<ISomeGenericClassBase>
}
Where the list will contain instances along these lines:
public interface ISomeGenericClassBase
{
}
public interface ISomeGenericBaseClass<T> : ISomeGenericBaseClass
{
}
public class SomeGenericClassBase : ISomeGenericClassBase
{
public static SomeGenericClassBase ConvertToMe(ISomeGenericClassBase target) {
return new SomeGenericClassBase() {Property1 = target.Property1; Property2 = target.Property2}
}
public static ISomeGenericBaseClass ExpantToTyped(SomeGenericClassBase target) {
// Implementation omitted - converts a base class instance to a generic instance by working out the generic type from saved data and reconstructing
}
}
public class SomeGenericClass<T> : SomeGenericClassBase, ISomeGenericBaseClass<T>
{
[XmlAttribute]
public string Name {get;set;}
[XmlAttribute]
public string Description{get;set;}
[XmlAttribute]
public T Value {get;set;}
[XmlElement("")]
public T[] ValidOptions {get;set;}
}
EDIT: Expanded the implementation - realised as it was, it didn't illustrate the problem correctly
Core issue is that I want to be able to serialize items that only implement the interface, even if I only get back SomeGenericClassBase instances. Per the approach used in the ExpandToTyped method, I'm expecting consumers of the class to save sufficient data in their implementations that allow the resulting classes to be converted back into their original form as required. So yes, there's a loss of fidelity, but it's one I can live with in exchange for the flexibility of using a list of interfaces instead of a list of base classes.
One solution is to sidestep the issue (IXmlSerializable.ReadXml looks quite painful anyway, e.g. for collections). What I eventually did is scrap IXmlSerializable, and instead generate a class along the lines of the below.
Please note that whilst this approach works, it's currently quite error prone if the serializable instance is used for anything other than serialization - synchronization is maintained ONLY when SerializationTarget is set or retrieved. When it's set, we convert existing parameters to appropriate instances and add them to a serializable list. When it's retrieved, if it's null, we inflate from whatever was in the current value.
However, if FooContainer changes after the creation of this object, it won't maintain that synchronization and what gets serialized will be out of date. This is largely because I'm lazy and don't want to implement IList<SomeGenericClassBase> again to override the Add and Remove methods (though this would be the more robust approach).
public class FooContainerSerializable
{
public FooContainerSerializable() {}
public FooContainerSerializable(FooContainer serializationTarget)
{
this.SerializationTarget = serializationTarget;
}
[XmlIgnore]
public FooContainer SerializationTarget
{
get {
if (_SerializationTarget == null)
{
_SerializationTarget = new FooContainer();
// Copy across extant collection properties here
this.Parameters.ForEach(item=>_SerializationTarget.Add(item));
}
return _SerializationTarget;
}
set {
// Synchronize this entity's entries here
_SerializationTarget = value;
_SerializationTarget.ForEach(item=>this.Parameters.Add(item.Deflate()));
}
}
private FooContainer _SerializationTarget;
[XmlElement]
public string FooName {
get {return this.SerializationTarget.FooName;}
set {this.SerializationTarget.FooName = value;}
}
[XmlElement]
public List<SomeGenericClassBase> Parameters {
get {return _Parameters ?? (_Parameters = new List<SomeGenericClassBase>());}
set {_Parameters = value;}
}
}
Here is another option if you are willing to use an abstract class instead of an interface in your collection definition. You'd also need to declare all the derived types of SomeGenericClassBase using XmlInclude attributes. I'm thinking this wouldn't be too bad if there are just a handful of types you'd use with this class.
[XmlRoot(ElementName = "FooContainer")]
public class FooContainer : List<SomeGenericClassBase>
{
[XmlAttribute]
public string FooName { get; set; }
}
[XmlInclude(typeof(SomeGenericClass<string>))]
[XmlInclude(typeof(SomeGenericClass<int>))]
public abstract class SomeGenericClassBase
{
[XmlAttribute]
public string Name { get; set; }
[XmlAttribute]
public string Description { get; set; }
}
public class SomeGenericClass<T> : SomeGenericClassBase
{
[XmlAttribute]
public T Value { get; set; }
[XmlElement]
public T[] ValidOptions { get; set; }
}
class Class1
{
public static void Run()
{
var f = new FooContainer()
{
new SomeGenericClass<string> { Name = "firstParam", Description = "First Paramater Serialized", Value = "Foobar"},
new SomeGenericClass<int> { Name = "nextParam", Description = "Second Serialized parameter", Value = 10000 }
};
f.FooName = "DoSomething";
XmlSerializer serializer = new XmlSerializer(f.GetType());
StringBuilder sb = new StringBuilder();
// Serialize
using (StringWriter writer = new StringWriter(sb))
{
serializer.Serialize(writer, f);
}
Console.WriteLine(sb);
// Deserialize
using(StringReader reader = new StringReader(sb.ToString()))
{
FooContainer f2 = (FooContainer)serializer.Deserialize(reader);
}
}
}
This would serialize to the following XML:
<?xml version="1.0" encoding="utf-16"?>
<FooContainer xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SomeGenericClassBase xsi:type="SomeGenericClassOfString" Name="firstParam" Description="First Paramater Serialized" Value="Foobar" />
<SomeGenericClassBase xsi:type="SomeGenericClassOfInt32" Name="nextParam" Description="Second Serialized parameter" Value="10000" />
</FooContainer>
Deserialization maintains full fidelity.
I'm trying to so some serialization using C#, but it throws an exception saying that there was an error generating the xml document. This is where I do the serialization:
public void serialize()
{
try
{
XmlSerializer ser = new XmlSerializer(typeof(Repository<Student>));
StreamWriter myWriter = new StreamWriter("stud.xml");
ser.Serialize(myWriter, rep);
myWriter.Close();
}
catch (Exception e)
{
Console.WriteLine("Error " + e.Message);
}
}
And this is the class I want to serialize:
public class Repository<T> : MyStack<T>
{
public int size;
public int capacity;
public SLL<T> stud;
public Repository()
{
/*
* Creator for class Repository.
*/
this.stud = new SLL<T>();
this.capacity = 20;
this.size = 0;
}
where MyStack is an interface, and SSL<T> is singly-linked list I have implemented.
Please add the full details of your exception to isolate the problem. From the code that you posted, it looks like your classes are not serializable.
Are the Repository, MyStack, SSL serializable classes (do you have the [Serializable] attribute on all the types of the members that will be serialized?)
It looks odd to serialise the repository class. Instead I would serialise the SLL<T> object, since that is the actual data you are interested in. The repository is just wrapper object for getting to data, thus there's no reason to serialise that.
As Cosmin mentions, make sure you place a [Serializable] attribute on SLL<>
[Serializable]
public class SLL<T>
{
}
Here is an example how to serialize. i think you might miss some important parts:
[Serializable]
public class User
{
[XmlElement("login")]
[Key]
public string login { get; set; }
[XmlElement("KDP")]
public int KDP { get; set; }
[XmlElement("attended")]
public int attended { get; set; }
etc.
the public getters and setters are important if you ever plan to deserialize.
and here is example how to serialize list of these:
[XmlArrayItem(typeof(User))]
[XmlElement("usersList")]
public static List<User> usersList = new List<User>();
using (StreamWriter myWriter = new StreamWriter(usersPath, false))
{
userSerializer.Serialize(myWriter, usersList);
myWriter.Close();
}
hope it helps.
Use [XmlIgnore] attribute for the property which is causing error generating the xml document, so that other things can be parsed.
Say I have the following class:
[Serializable]
public class A
{
public string B { get; set; }
}
and the following method was used to serialize it:
public void Serialize()
{
BinaryFormatter b = new BinaryFormatter();
b.Serialize(new FileStream(#"C:\Temp\out.dat", FileMode.Create), new A());
}
If at some point, someone came along and modified the class definition to contain an extra property (or remove one):
[Serializable]
public class A // Same class
{
public string B { get; set; }
public string C { get; set; } // New property
}
then the following will break:
public void Deserialize()
{
BinaryFormatter b = new BinaryFormatter();
A deserialized = (A)b.Deserialize(new FileStream(#"C:\Temp\out.dat", FileMode.Open));
}
because the serialized version of the class does not match the class definition of the new class.
I really dislike the idea of serialization as a persistence mechanism because it's so fragile. I would have dealt with this much earlier if I had been involved in the decision.
Is there any way to get my serialized data into a form I can read it without reverting all of my current class definitions to their serialized state?
Even if it's hacky "to the max", I was hoping I could do this, because I would hopefully only do it once (or until I could figure out how to fix the root of the problem).
edit
There are dozens of these classes that have been serialized and then modified in my system already. It is not feasible to use version control to see exactly when and how each individual class changed.
I'm currently trying to figure out a way I can retain "old A"'s settings before the user tries to deserialize the object to the "new A" format and I have to 1) try, 2) catch, 3) swallow the error, and finally 4) recreate A with default values (based on the new object definition).
I believe that you can decorate newly added members with the OptionalFieldAttribute (System.Runtime.Serialization namespace), which will allow you to deserialize instances that were serialized before that member was added.
[Serializable]
public class A // Same class
{
public string B { get; set; }
[OptionalField] public string C { get; set; } // New property
}