So i need to xml serialize a generic list of objects where the object consists of another list and a string.
This is what i get now
<?xml version="1.0" encoding="UTF-8"?>
-<ArrayOfRecept xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-<Recept>
<Namn>Nomnom</Namn>
</Recept>
-<Recept>
<Namn>Ännu mer nomnom</Namn>
</Recept>
</ArrayOfRecept>
Here only the string value is serialized and not my List
This is my object that i want to serialize
public class Recept
{
private ListHanterare<string> ingredienser;
private string namn;
public Recept()
{
ingredienser = new ListHanterare<string>();
}
public ListHanterare<string> Ingredienser
{
get { return ingredienser; }
}
public string Namn
{
get { return namn; }
set { namn = value; }
}
}
So I will have a list of Recept that i want to xml serialize and I want the xml to show both "Namn" and the "Ingredienser"-list.
This is my serializer
class XMLSerial
{
public static bool Serialize<T>(T obj, string filePath)
{
bool bok = true;
XmlSerializer serializer = new XmlSerializer(typeof(T));
TextWriter writer = new StreamWriter(filePath);
try
{
serializer.Serialize(writer, obj);
}
catch
{
bok = false;
}
finally
{
if (writer != null)
writer.Close();
}
return bok;
}
}
This is inside the ListHanterare class where I pass the object to the serializer
public bool XMLSerialize(string filePath){
return XMLSerial.Serialize<List<T>>(lista, filePath);
EDIT:
So by adding a setter to ingredienser I now get this
-<Recept>
<Ingredienser/>
<Namn>rec</Namn>
</Recept>
But ingredienser is still empty
My ListHanterare class is a basic generic List class
public class ListHanterare<T> : IListHanterare<T>
{
private List<T> lista; // This is the list
private int count;
public ListHanterare()
{
lista = new List<T>();
}
So i need to serialize a ListHanterare list of Recept objects where the Recept object consists of a string and another ListHanterare list of strings, the string is serialized correctly but not the list of strings.
In order for XmlSerializer to serialize or deserialize the property Ingredienser of your Recept class, it must have a public setter as well as a public getter:
public ListHanterare<string> Ingredienser
{
get { return ingredienser; }
set { ingredienser = value; }
}
If you don't do this, XmlSerializer will ignore the property.
You might still have problems with the class ListHanterare, but it's not shown in your question.
DataContractSerializer does not have this requirement. However, to serialize and deserialize your classes with DataContractSerializer and include private fields or properties, you would need to fully annotate them with data contract attributes. Private field and properties marked with [DataMember] get serialized despite being private. Data contract serialization is opt-in, however, so to use this feature your classes would need to be fully attributed.
Update
You don't show the complete code of your ListHanterare<string> class. In order for XmlSerializer to serialize and deserialize it successfully, it must either:
Make all its contents available in publicly settable and gettable properties, or
Implement ICollection<T> (or even IList<T>) as described here: XmlSerializer Class.
For method #1, you could modify your class as follows:
public class ListHanterare<T>
{
private List<T> lista; // This is the list
private int count;
public ListHanterare()
{
lista = new List<T>();
}
[XmlElement("Item")]
public T[] SerializableList
{
get
{
return (lista == null ? new T [0] : lista.ToArray());
}
set
{
if (lista == null)
lista = new List<T>();
lista.Clear();
lista.AddRange(value ?? Enumerable.Empty<T>());
count = lista.Count;
}
}
}
You will now see the items serialized your XML.
Related
I want to use the .NET XmlSerializer class to serialize a class object to an XML string. both null string and empty string should be serialized.
if a property value is null then XML format like
<property />
if a property value is an empty string, then the serialized XML format is
<property></property>
When you need custom serialization, you'll need to implement your own implementation of the IXmlSerialization interface. This provides custom formatting for XML serialization and deserialization. For example :
public class Person : IXmlSerializable
{
// Private state
private string personName;
// Constructors
public Person (string name)
{
personName = name;
}
public Person ()
{
personName = null;
}
// Xml Serialization Infrastructure
public void WriteXml (XmlWriter writer)
{
writer.WriteString(personName);
}
public void ReadXml (XmlReader reader)
{
personName = reader.ReadString();
}
public XmlSchema GetSchema()
{
return(null);
}
// Print
public override string ToString()
{
return(personName);
}
}
You can use properties of class without initialize. It will like:
class XmlData{
public string Person;
public XmlData(){
Person=null;
}
}
class Program{
static void Main(){
var xmlTaplate=new XmlData();//in this step property Person is null
var serializer = new XmlSerializer(typeof(xmlTemplate));
using (TextWriter writer = new StreamWriter(#"person.xml"))
{
serializer.Serialize(writer, details);
}
}
}
I know that an XmlSerializer will throw an exception if the passed in type does not match the type from when the serializer was created.
My question is: Is it possible to serialize a base type without knowing the derived type? Consider this scenario:
The following classes are in a top-level domain and have few or no dependencies:
public class Serializer
{
private static string Serialize(Base baseData)
{
var serializer = new XmlSerializer(typeof(Base));
var sb = new StringBuilder();
using (var writer = XmlWriter.Create(sb))
{
serializer.Serialize(writer, baseData);
}
return sb.ToString();
}
private static string Deserialize(...) { ... }
}
public class Base
{
public string Name { get; set; }
}
The following derived class has additional dependencies on things like UI components, composition, etc. (more on this later):
[Export(typeof(IMyComponent))]
public class Derived : Base, IMyComponent, INotifyPropertyChanged
{
public new string Name
{
get { return base.Name; }
set
{
base.Name = value;
NotifyPropertyChanged("Name");
}
}
}
If I invoke the serializer like this:
Base myData = new Derived();
myData.Name = "Foo bar";
Serializer.Serialize(myData);
It throws an exception because I passed in a Derived, even though the parameters and serializer are explicitly set to Base. All I want is to serialize Base, I don't care about whatever is in Derived.
My goal here is to create the derived class with a UI, but only serialize the base class. The reason is that I am deserializing the XML in a separate assembly that must not contain additional dependencies beyond the .NET framework. The serializer and base objects are recreated/copied into this isolated assembly so that the namespaces and types will match, so that when deserialization occurs, the types will be deserialized correctly. But I can't include a copy of Derived because it has dependencies on UI, MEF, other third party libraries, etc.
So, how can I force the serializer to serialize the base class even if I pass in a derived class? Again, I don't care if I lose the information in Derived, all I am concerned about is Base.
Since you are looking to only serialize the Base properties, you have to create an instance of Base and map the properties from the derived type. Serializers are programmed to be smart, sometimes too smart in your case.
Look into AutoMapper or just do it manually.
public class Serializer
{
private static string Serialize(Base baseData)
{
var mappedBase = new Base();
// Do mapping
mappedBase.Foo = baseData.Foo;
var serializer = new XmlSerializer(typeof(Base));
var sb = new StringBuilder();
using (var writer = XmlWriter.Create(sb))
{
serializer.Serialize(writer, mappedBase);
}
return sb.ToString();
}
private static string Deserialize(...) { ... }
}
One possible solution would be:
public class Serializer
{
public static string Serialize(Base baseData)
{
var baseType = baseData.GetType().BaseType;
var baseTypeFieldInfo = baseType.GetProperties();
var newBaseInstance = new Base();
var newBaseInstanceType = newBaseInstance.GetType();
//ONLY gets public properties but could be applied to fields
var newBaseTypeFieldInfo = newBaseInstanceType.GetProperties();
foreach (var bt in baseTypeFieldInfo)
{
foreach (var nbt in newBaseTypeFieldInfo)
{
if (nbt.Name == bt.Name)
{
nbt.SetValue(newBaseInstance, bt.GetValue(baseData, null), null);
}
}
}
var serializer = new XmlSerializer(typeof(Base));
var sb = new StringBuilder();
using (var writer = XmlWriter.Create(sb))
{
serializer.Serialize(writer, newBaseInstance);
}
return sb.ToString();
}
}
public class Base
{
public string Name { get; set; }
}
public class Derived : Base
{
public string AnotherName { get; set; }
public new string Name
{
get { return base.Name; }
set { base.Name = value; }
}
}
I have this code:
[DataContract]
class MyData
{
private Int32 dato1;
[DataMember]
public Int32 Dato1
{
get { return dato1; }
set { dato1 = value; }
}
public MyData(Int32 dato1)
{
this.dato1 = dato1;
}
public MyData()
{
this.dato1 = 0;
}
}
[DataContract]
class MyCollection2 : List<MyData>
{
public MyCollection2()
{
}
}
Then I try to serialize one object of MyCollection2 with:
MyCollection2 collec2 = new MyCollection2();
collec2.Add(new MyData(10));
DataContractSerializer ds = new DataContractSerializer(typeof(MyCollection2));
using (Stream s = File.Create(dialog.FileName))
{
ds.WriteObject(s, collec2);
}
Then I get the next exception:
InvalidDataContractException is an invalid collection type since it
have DataContractAttribute
However, if I use the next class (doesn't inherit directly from List, instead has a List member):
[DataContract]
class MyCollection1
{
[DataMember]
public List<MyData> items;
public MyCollection1()
{
items = new List<MyData>();
}
}
Here Serialization works ok. Do you know why ?.
Thanks very much.
Use [CollectionDataContract(...)] instead of [DataContract]. For more details see here.
For full details see here.
I have a List<object> with different types of objects in it like integers, strings, and custom types. All custom types are protobuf-adjusted.
What I wanna do now is to serialize / deserialize this list with protobuf.net. Up until now I suspect that I have to declare each and every type explicitly, which is unfortunately not possible with these mixed-list constructs. Because the binary formater has no problems to do these things I hope that I missed something and that you can help me out.
So my question is how to deal with objects in protobuf.net.
(disclosure: I'm the author of protobuf-net)
BinaryFormatter is a metadata-based serializer; i.e. it sends .NET type information about every object serialized. protobuf-net is a contract-based serializer (the binary equivalent of XmlSerializer / DataContractSerializer, which will also reject this).
There is no current mechanism for transporting arbitrary objects, since the other end will have no way of knowing what you are sending; however, if you have a known set of different object types you want to send, there may be options. There is also work in the pipeline to allow runtime-extensible schemas (rather than just attributes, which are fixed at build) - but this is far from complete.
This isn't ideal, but it works... it should be easier when I've completed the work to support runtime schemas:
using System;
using System.Collections.Generic;
using ProtoBuf;
[ProtoContract]
[ProtoInclude(10, typeof(DataItem<int>))]
[ProtoInclude(11, typeof(DataItem<string>))]
[ProtoInclude(12, typeof(DataItem<DateTime>))]
[ProtoInclude(13, typeof(DataItem<Foo>))]
abstract class DataItem {
public static DataItem<T> Create<T>(T value) {
return new DataItem<T>(value);
}
public object Value {
get { return ValueImpl; }
set { ValueImpl = value; }
}
protected abstract object ValueImpl {get;set;}
protected DataItem() { }
}
[ProtoContract]
sealed class DataItem<T> : DataItem {
public DataItem() { }
public DataItem(T value) { Value = value; }
[ProtoMember(1)]
public new T Value { get; set; }
protected override object ValueImpl {
get { return Value; }
set { Value = (T)value; }
}
}
[ProtoContract]
public class Foo {
[ProtoMember(1)]
public string Bar { get; set; }
public override string ToString() {
return "Foo with Bar=" + Bar;
}
}
static class Program {
static void Main() {
var items = new List<DataItem>();
items.Add(DataItem.Create(12345));
items.Add(DataItem.Create(DateTime.Today));
items.Add(DataItem.Create("abcde"));
items.Add(DataItem.Create(new Foo { Bar = "Marc" }));
items.Add(DataItem.Create(67890));
// serialize and deserialize
var clone = Serializer.DeepClone(items);
foreach (DataItem item in clone) {
Console.WriteLine(item.Value);
}
}
}
List<YourClass> list;
ProtoBuf.Serializer.Deserialize<List<YourClass>>(filestream);
There is a way of doing this, albeit not a very clean way, by using a wrapper object that utilises another serialisation mechanism that supports arbitrary objects. I am presenting an example below using JSON but, like I said, resorting to a different serialisation tool seems like defeating the purpose of using protobuf:
[DataContract]
public class ObjectWrapper
{
[DataMember(Order = 1)]
private readonly string _serialisedContent;
[DataMember(Order = 2)]
private readonly string _serialisedType;
public object Content { get; private set; }
[UsedImplicitly]
private ObjectWrapper() { }
public ObjectWrapper(object content)
{
_serialisedContent = JsonConvert.SerializeObject(content);
_serialisedType = content.GetType().FullName;
Content = content;
}
[ProtoAfterDeserialization]
private void Initialise()
{
var type = Type.GetType(_serialisedType);
Content = type != null
? JsonConvert.DeserializeObject(_serialisedContent, type)
: JsonConvert.DeserializeObject(_serialisedContent);
}
}
EDIT: This can also be done using C#'s built-in binary serialisation
[DataContract]
public class ObjectWrapper
{
[DataMember(Order = 1)]
private readonly string _serialisedContent;
public object Content { get; private set; }
[UsedImplicitly]
private ObjectWrapper() { }
public ObjectWrapper(object content)
{
using (var stream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, content);
stream.Flush();
stream.Position = 0;
_serialisedContent = Convert.ToBase64String(stream.ToArray());
}
}
[ProtoAfterDeserialization]
private void Initialise()
{
var data = Convert.FromBase64String(source);
using (var stream = new MemoryStream(data))
{
var formatter = new BinaryFormatter();
stream.Seek(0, SeekOrigin.Begin);
return formatter.Deserialize(stream);
}
}
}
The setup:
class Item
{
private int _value;
public Item()
{
_value = 0;
}
public int Value { get { return _value; } set { _value = value; } }
}
class ItemCollection : Collection<Item>
{
private string _name;
public ItemCollection()
{
_name = string.Empty;
}
public string Name { get {return _name;} set {_name = value;} }
}
Now, trying to serialize using the following code fragment:
ItemCollection items = new ItemCollection();
...
XmlSerializer serializer = new XmlSerializer(typeof(ItemCollection));
using (FileStream f = File.Create(fileName))
serializer.Serialize(f, items);
Upon looking at the resulting XML I see that the ItemCollection.Name value is not there!
I think what may be happening is that the serializer sees the ItemCollection type as a simple Collection thus ignoring any other added properties...
Is there anyone having encountered such a problem and found a solution?
Regards,
Stécy
This behavior is "By Design". When deriving from a collection class the Xml Seralizier will only serialize the collection elements. To work around this you should create a class that encapsulates the collection and the name and have that serialized.
class Wrapper
{
private Collection<Item> _items;
private string _name;
public Collection<Item> Items { get {return _items; } set { _items = value; } }
public string Name { get { return _name; } set { _name = value; } }
}
A detailed discussion is available here: http://blogs.vertigo.com/personal/chris/Blog/archive/2008/02/01/xml-serializing-a-derived-collection.aspx
XmlSerializer is evil. That said, any object that implements IEnumerable will be serialized as an simple collection, ignoring any extra properties you've added yourself.
You will need to create a new class that holds both your property and a property that returns the collection.
I am not sure if I am missing something, but do you want the resulting xml to be
<ItemCollection>
<Name>name val</Name>
<Item>
<Value>1</alue>
</Item
<Item>
<Value>2</alue>
</Item
</ItemCollection>
If so, just apply the XmlRoot attribute to the itemcollection class and set the element name...
[XmlRoot(ElementName="ItemCollection")]
public class ItemCollection : Collection<Item>
{
[XmlElement(ElementName="Name")]
public string Name {get;set;}
}
This will instruct the serializer to output the required name for you collection container.
You can also try to implelemnt your own serialization using IXmlSerializable interface
public class ItemCollection : Collection<Item>,IXmlSerializable
{
private string _name;
public ItemCollection()
{
_name = string.Empty;
}
public string Name
{
get { return _name; }
set { _name = value; }
}
#region IXmlSerializable Members
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
}
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteElementString("name", _name);
List<Item> coll = new List<Item>(this.Items);
XmlSerializer serializer = new XmlSerializer(coll.GetType());
serializer.Serialize(writer, coll);
}
#endregion
}
Above code will generate the serialized xml as
<?xml version="1.0"?>
<ItemCollection>
<name />
<ArrayOfItem xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Item>
<Value>1</Value>
</Item>
<Item>
<Value>2</Value>
</Item>
</ArrayOfItem>
</ItemCollection>
public class Animals : List<Animal>, IXmlSerializable
{
private static Type[] _animalTypes;//for IXmlSerializable
public Animals()
{
_animalTypes = GetAnimalTypes().ToArray();//for IXmlSerializable
}
// this static make you access to the same Animals instance in any other class.
private static Animals _animals = new Animals();
public static Animals animals
{
get {return _animals; }
set { _animals = value; }
}
#region IXmlSerializable Members
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
bool wasEmpty = reader.IsEmptyElement;
reader.Read();
if (wasEmpty)
return;
reader.MoveToContent();
reader.ReadStartElement("Animals");
// you MUST deserialize with 'List<Animal>', if Animals class has no 'List<Animal>' fields but has been derived from 'List<Animal>'.
List<Animal> coll = GenericSerializer.Deserialize<List<Animal>>(reader, _animalTypes);
// And then, You can set 'Animals' to 'List<Animal>'.
_animals.AddRange(coll);
reader.ReadEndElement();
//Read Closing Element
reader.ReadEndElement();
}
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteStartElement("Animals");
// You change 'List<Animal>' to 'Animals' at first.
List<Animal> coll = new List<Animal>(_animals);
// And then, You can serialize 'Animals' with 'List<Animal>'.
GenericSerializer.Serialize<List<Animal>>(coll, writer, _animalTypes);
writer.WriteEndElement();
}
#endregion
public static List<Type> GetAnimalTypes()
{
List<Type> types = new List<Type>();
Assembly asm = typeof(Animals).Assembly;
Type tAnimal = typeof(Animal);
//Query our types. We could also load any other assemblies and
//query them for any types that inherit from Animal
foreach (Type currType in asm.GetTypes())
{
if (!currType.IsAbstract
&& !currType.IsInterface
&& tAnimal.IsAssignableFrom(currType))
types.Add(currType);
}
return types;
}
}