How to serialize object to xml with circular dependency in c#? - c#

I've got an object which has a circular dependency
public class Levels
{
public UserDescription user { get; set; }
public List<Levels> friends {get; set;}
public Levels(UserDescription user, List<Levels> friends)
{
this.user = user;
this.friends = friends;
}
public Levels() { }
}
I need to serialize it to xml, so I do the following:
public string SerializeObject(object obj)
{
System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
serializer.Serialize(ms, obj);
ms.Position = 0;
xmlDoc.Load(ms);
return xmlDoc.InnerXml;
}
}
This code throws an exception System.InvalidOperationException onserializer = new System.Xml.Serialization.XmlSerializer. How can I solve this?

The problem was that class UserDescription didn't have an empty constructor.

Related

Class with nested classes throws InvalidOperationException: Token StartElement in state Epilog would result in an invalid XML document

I am trying to Serialize a simple class with a single property for a SOAP request using System.Xml.Serialization.XmlSerializer which works fine, but as soon as I add another property to my class and the property type is another class I receive this error message when executing XmlSerializer.Serialize(StringWriter, myclass):
InvalidOperationException: Token StartElement in state Epilog would result in an invalid XML document.
The two classes are dead-simple:
[XmlRoot(Namespace = "http://example.org/", ElementName = "Foo")]
public class Foo
{
public string Id { get; set; }
public Bar Bar { get; set; }
}
public class Bar
{
public string Id { get; set; }
}
This is how I perform the serialization:
var foo = new Foo(Id = "foo-id", Bar = new Bar { Id = "bar-id" });
var soapReflectionImporter = new SoapReflectionImporter("http://example.org");
var xmlTypeMapping = soapReflectionImporter.ImportTypeMapping(typeof(Foo));
var serializer = new XmlSerializer(xmlTypeMapping);
using (var writer = new StringWriter())
{
serializer.Serialize(writer, foo);
}
If I remove the Bar property from Foo, everything works as expected. I already looked through this and this without it solving the issue. I also cannot simply call the constructor for XmlSerializer with a type parameter because I need the XmlTypeMapping for the SOAP request. Can someone point out where the issue is or if I am missing some additional configuration?
The output XML needs a root element. Using XmlWriter along with XmlWriterSettings can help, like below.
[XmlRoot(Namespace = "http://example.org/", ElementName = "Foo")]
public class Foo
{
public string Id { get; set; }
public Bar Bar { get; set; }
}
public class Bar
{
public string Id { get; set; }
}
public class Program
{
static void Main(string[] args)
{
var foo = new Foo() { Id = "foo-id", Bar = new Bar { Id = "bar-id" } };
var soapReflectionImporter = new SoapReflectionImporter("http://example.org");
var xmlTypeMapping = soapReflectionImporter.ImportTypeMapping(typeof(Foo));
var serializer = new XmlSerializer(xmlTypeMapping);
//using (var writer = new StringWriter())
//{
// serializer.Serialize(writer, foo);
//}
Stream st = new MemoryStream();
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.WriteEndDocumentOnClose = true;
XmlWriter writer = XmlWriter.Create(st, settings);
writer.WriteStartElement("base");
serializer.Serialize(writer, foo);
writer.Close(); //will write the final closing tag
st.Position = 0;
StreamReader sr = new StreamReader(st);
string data = sr.ReadToEnd();
}
}

c# serialize tree of objects

I have application, which must contain map. To realize this task I create some classes:
1. Map (contains Image and two objects of Location class)
2. GPSPoint (contains two objects of ICoordinate)
3. ImagePoint (contains two int variables)
4. Location (contains GPSPoint and ImagePoint)
And one interface, and two classes, which realize it:
1. ICoordinate
2. GpsCoordinateDeg
3. GpsCoordinateDegMinSec
All of them implements ISerialization interface and have public void GetObjectData(SerializationInfo, StreamingContext) methods.
I want to save my map in the file, and I realize one of the methods of serialization, but it isn't work - I get void xml file:
<?xml version="1.0"?>
<Map xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
I use my class for serialization in this code:
[Serializable]
class Map : ISerializable {
...
public void saveInFile(string filepath) {
Serializer serializer = new Serializer();
serializer.SerializeObject(this, filepath);
}
...
}
This is code of my Serializer:
class Serializer {
public void SerializeObject<T>(T serializableObject, string fileName) {
if (serializableObject == null) { return; }
try {
XmlDocument xmlDocument = new XmlDocument();
XmlSerializer serializer = new XmlSerializer(serializableObject.GetType());
using (MemoryStream stream = new MemoryStream()) {
serializer.Serialize(stream, serializableObject);
stream.Position = 0;
xmlDocument.Load(stream);
xmlDocument.Save(fileName);
stream.Close();
}
} catch (Exception ex) {
//Log exception here
}
}
public T DeSerializeObject<T>(string fileName) {
if (string.IsNullOrEmpty(fileName)) { return default(T); }
T objectOut = default(T);
try {
string attributeXml = string.Empty;
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(fileName);
string xmlString = xmlDocument.OuterXml;
using (StringReader read = new StringReader(xmlString)) {
Type outType = typeof(T);
XmlSerializer serializer = new XmlSerializer(outType);
using (XmlReader reader = new XmlTextReader(read)) {
objectOut = (T)serializer.Deserialize(reader);
reader.Close();
}
read.Close();
}
} catch (Exception ex) {
//Log exception here
}
return objectOut;
}
}
Where is the problem?
As i dont know the complete implementation of your poco classes i suggest you to look further to your GPSPoint class:
GPSPoint (contains two objects of ICoordinate)
You can not serialize an interface. The problem is that an interface is an opaque type. There is no way for the serializer to know what to write out and more importantly what to create when it needs to serialize things back.
You may look into StackOverflow for "serializing interfaces"-postings. I hope it helps.
Bad idea going for generics with serialization since it heavily relies on object boxing and unboxing.
My way of doing this does not rely on implementing ISerializable, rather on the careful use of attributes.
Decorate each class below with [Serializable()], [XmlType(AnonymousType=true)], [XmlRoot(Namespace="", IsNullable=false)]. The enum stands for coordinate types. Derive your existing classes from these ones. Mark properties you do not want serialized with [XmlIgnore] in your derived classes. Make Coordinate below implement your ICoordinate.
public partial class Location {
[XmlElement]
public GPSPoint GPSPoint { get; set; }
[XmlElement]
public ImagePoint ImagePoint { get; set; }
}
public partial class GPSPoint {
[XmlElement(ElementName = "Coordinate", Order = 0)]
public Coordinate Coordinate1 {get; set; }
[XmlElement(Order=1, ElementName="Coordinate")]
public Coordinate Coordinate2 {get;set;}
}
public partial class Coordinate {
[XmlAttribute()]
public ICoordinateType type {get;set;}
}
[Serializable]
public enum ICoordinateType {
/// <remarks/>
GpsCoordinateDegMinSec,
/// <remarks/>
GpsCoordinateDeg,
}
public partial class Map {
[XmlElement(Order=0, IsNullable=true)]
public object Image { get; set; }
/// <remarks/>
[XmlElement(ElementName="Location", Order = 1)]
public Location Location1 {get; set; }
/// <remarks/>
[XmlElement(ElementName = "Location", Order = 2)]
public Location Location2 {get; set; }
}
This is tested and running:
var tc1 = new xyz.Coordinate () {type = xyz.ICoordinateType.GpsCoordinateDeg};
var tc2 = new xyz.Coordinate () {type = xyz.ICoordinateType.GpsCoordinateDegMinSec};
var l1 = new xyz.Location() {
GPSPoint = new xyz.GPSPoint() { Coordinate1 = tc1, Coordinate2 = tc2 },
ImagePoint = new xyz.ImagePoint() { x = 0, y = 0 } };
xyz.Map m = new xyz.Map() {
Image = null,
Location1 = l1,
Location2 = new xyz.Location() {
GPSPoint = new xyz.GPSPoint() {
Coordinate1 = tc1, Coordinate2 = tc2 },
ImagePoint = new xyz.ImagePoint() { x = 1, y = 2 }
} };
XmlSerializer xs = new XmlSerializer(typeof(Map));
using (var fs = File.Create("map.xml") ) { xs.Serialize(fs, m); }

Error when deserializing

I'm trying to write some code to deserialize an XML file. I've looked around a bit and found something which has led me to the following method:
public static void Deserialize(string filePath)
{
RootObject ro = null;
string path = filePath;
XmlSerializer serializer = new XmlSerializer(typeof(RootObject));
StreamReader reader = new StreamReader(path);
ro = (RootObject) serializer.Deserialize(reader);
reader.Close();
}
But all I get is this error and I'm not sure what's causing it:
There is an error in XML document (2, 2).
The RootObject you see in Deserialize() is this one: I'm new to XMl serializing/deserializing so I'm not sure if I've defined it 100% correctly.
public class RootObject
{
public Services Services { get; set; }
}
public class Services
{
public Service TileMapService { get; set; }
}
public class Service
{
public string Title { get; set; }
public string href { get; set; }
}
I'm using this method to create the XML files in the first place and it seems to work fine:
public static void WriteToXmlFile<T>(string filePath, T objectToWrite) where T : new()
{
TextWriter writer = null;
try
{
var serializer = new XmlSerializer(typeof (T));
writer = new StreamWriter(filePath);
serializer.Serialize(writer, objectToWrite);
}
finally
{
if (writer != null)
{
writer.Close();
}
}
}
It gets me an XML file that looks like this:
<RootObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Services>
<TileMapService>
<Title>Some title</Title>
<href>http://something</href>
</TileMapService>
</Services>
</RootObject>
Your code works fine. The "There is an error in XML document (2, 2)." might be because your actual file (not the one created by WriteToXmlFile<T>) has whitespace at the start, or is using the wrong namespace. Check the .InnerException for more detail, or (perhaps simpler) please post the actual xml file contents. Example of it working fine (along with some recommended tweaks to the two key methods):
static void Main()
{
RootObject obj = new RootObject
{
Services = new Services
{
TileMapService = new Service
{
Title = "abc",
href = "def"
}
}
};
WriteToXmlFile("foo.xml", obj);
var loaded = Deserialize<RootObject>("foo.xml");
var svc = loaded.Services.TileMapService;
System.Console.WriteLine(svc.Title); // abc
System.Console.WriteLine(svc.href); // def
}
public static void WriteToXmlFile<T>(string filePath, T objectToWrite)
{
var serializer = new XmlSerializer(typeof(T));
using (var writer = new StreamWriter(filePath))
{
serializer.Serialize(writer, objectToWrite);
}
}
public static T Deserialize<T>(string filePath)
{
var serializer = new XmlSerializer(typeof(T));
using (var reader = new StreamReader(filePath))
{
return (T)serializer.Deserialize(reader);
}
}

DataContractJsonSerializer generic list containing element type's subtypes

I'm going to use DataContractJsonSerializer for JSON serialization/deserialization.
I have two object types in the JSON array and want them both to be deserialized into corresponding object types.
Having the following class defnitions
[DataContract]
public class Post {
[DataMember(Name = "content")]
public String Content { get; set; }
}
[DataContract]
public class User {
[DataMember(Name = "user_name")]
public String UserName { get; set; }
[DataMember(Name = "email")]
public String Email { get; set; }
}
[DataContract]
public class Container {
[DataMember(Name="posts_and_objects")]
public List<Object> PostsAndUsers { get; set; }
}
how can I detect them and deserialize both into the corresponding object type and store in PostsAndUsers property?
If you specific the known types you can serialize and deserialize your class Container, try this code:
protected static Stream GetStream<T>(T content)
{
MemoryStream memoryStream = new MemoryStream();
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T), new []{typeof(Post), typeof(User)});
serializer.WriteObject(memoryStream, content);
memoryStream.Seek(0, SeekOrigin.Begin);
return memoryStream;
}
protected static T GetObject<T>(Stream stream)
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T), new[] { typeof(Post), typeof(User) });
return (T)serializer.ReadObject(stream);
}
static void Main(string[] args)
{
var container = new Container {PostsAndUsers = new List<object>()};
container.PostsAndUsers.Add(new Post{Content = "content1"});
container.PostsAndUsers.Add(new User{UserName = "username1"});
container.PostsAndUsers.Add(new Post { Content = "content2" });
container.PostsAndUsers.Add(new User { UserName = "username2" });
var stream = GetStream(container);
var parsedContainer = GetObject<Container>(stream);
foreach (var postsAndUser in parsedContainer.PostsAndUsers)
{
Post post;
User user;
if ((post = postsAndUser as Post) != null)
{
// is post
}
else if ((user = postsAndUser as User) != null)
{
// is user
}
else
{
throw new Exception("");
}
}
}

Serialize ArrayList of Objects

I have an ArrayList which stores a custom object. I want to serialize that ArrayList to a string so I can save it inside the Application settings.
This question looks to resolve it, but is in java. And I am not smart with XML, so could someone help out?
Serialize an ArrayList of Date object type
I have my ArrayList setup:
...
MyObject tempObj = new MyObject("something",1,"something");
MyCollection.Add(tempObj);
...
And I originally had this. It outputs the string, but the object isn't there:
private string SerializeArrayList(ArrayList obj)
{
System.Xml.XmlDocument doc = new XmlDocument();
Type[] extraTypes = new Type[1];
extraTypes[0] = typeof(MyObject);
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(ArrayList), extraTypes);
System.IO.MemoryStream stream = new System.IO.MemoryStream();
try
{
serializer.Serialize(stream, obj);
stream.Position = 0;
doc.Load(stream);
return doc.InnerXml;
}
catch { throw; }
finally
{
stream.Close();
stream.Dispose();
}
}
EDIT: Code request
public class MyObject
{
private string eN;
private Boolean bE;
private int min;
private Boolean bot;
private string onE;
public MyObject(string na, Boolean b)
{
...
}
public MyObject()
{
}
public string GetSomething()
{
...
I tested your code and it seems to work ok, as long as you have [Serializable] on your object.
Also if you are trying to Serialize the fields, you will have to make them public properties.
My Test:
ArrayList array = new ArrayList();
Rules tempObj = new Rules { onE = "Value", min = 45, eN = "Value" };
array.Add(tempObj);
string result = SerializeArrayList(array);
private string SerializeArrayList(ArrayList obj)
{
XmlDocument doc = new XmlDocument();
XmlSerializer serializer = new XmlSerializer(typeof(ArrayList), new Type[]{typeof(Rules)});
using (MemoryStream stream = new System.IO.MemoryStream())
{
try
{
serializer.Serialize(stream, obj);
stream.Position = 0;
doc.Load(stream);
return doc.InnerXml;
}
catch (Exception ex)
{
}
}
return string.Empty;
}
Object:
[Serializable]
[XmlType(TypeName = "Rules")]
public class Rules
{
// Make fields propertys so they will be serialized
public string eN { get; set; } //Name
public Boolean bE { get; set; } //Whether blocked entirely
public int min { get; set; } //Minutes they are allowed if blocked
public Boolean bot { get; set; } //Create notification if allowance exceed
public string onE { get; set; } //Nothing or CLOSE Process
public Rules(string na, Boolean b)
{
}
public Rules()
{
}
}
I ran into a similar problem, and there is this great program called SharpSerializer, which is available through Nuget. It will handle your ArrayList quite easily, just type the code:
SharpSerializer mySerializer = new SharpSerializer();
mySerializer.Serialize(ArrayList, "filetosaveto.xml");
Here's the link to the website, its free so don't worry about paying anything:
http://www.sharpserializer.com/en/index.html

Categories