Serialize an Object to XML: IList<CustomObject> Property Causes Exception - c#

I am using the following function to attempt to serialize an object to XML..
public static string SerializeObject<T>(T obj)
{
try
{
string xmlString = null;
MemoryStream memoryStream = new MemoryStream();
XmlSerializer xs = new XmlSerializer(typeof(T));
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
xs.Serialize(xmlTextWriter, obj);
memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
xmlString = UTF8ByteArrayToString(memoryStream.ToArray()); return xmlString;
}
catch (Exception ex)
{
return string.Empty;
}
}
When attempting to serialize an object that has an IList property in it, I get the following exception..
Cannot serialize member 'ObjectModel.Order.LineItems' of type 'System.Collections.Generic.IList
Can someone help me change my function to accommodate for this scenario?
Is there anything I can do this existing code to look into the input object. If its of type Ilist change it to a List? Can somoeone help me with code for that if its at all possible??

There's no great solution for this, only the workaround of using a concrete type like List<T> in this case - you could either change the existing property to be List<T> or add an additional property used just for serialization of type List<T> (and XML-ignore your existing property).

XmlSerializer does not handle properties of type IList<T>. There are some workarounds, the most straightforward of which is to change the type of the property:
https://www.google.com/search?q=xmlserializer+ilist

How important is Xml output? binary format is more accomodating. you could convert the output to base64 string if needed.
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.formatters.binary.binaryformatter(v=vs.71).aspx

Related

Deserialize Dictionary<> using DataContractSerializer

I need to deserialize Dictionary<> in C#. Since, XmlSerializer cannot be used for generic Dictionary type, I thought of using DataContractSerializer.I had a StreamReader in which the data was to be deserialized, but it showed unmatching overload parameters on using with DataContractSerializer.ReadObject.
My previous code was:
StreamReader isr = new StreamReader(File.OpenRead(algorithmName + ".conf"));
configuration.Load(isr);
The Load method is defined as:
public void Load(StreamReader isr)
{
DataContractSerializer dcs = new DataContractSerializer(typeof(the class containing this method));
XmlDictionaryReader read = XmlDictionaryReader.CreateTextReader(isr, new XmlDictionaryReaderQuotas());
dcs.ReadObject(isr);
}
The Load method is contained in class that implements Dictionary<>
But this showed parameter mismatched error due to StreamReader as parameter for ReadObject().
So, I modified the code as:
FileStream isr = File.OpenRead(algorithmName + ".conf");
configuration.Load(isr);
And the Load method as:
public void Load(FileStream isr)
{
DataContractSerializer dcs = new DataContractSerializer(typeof(the class containing Load method));
XmlDictionaryReader read = XmlDictionaryReader.CreateTextReader(isr, new XmlDictionaryReaderQuotas());
dcs.ReadObject(isr);
}
Now there are no errors but I wanted to know whether using DataContractSerializer in place of XmlSerializer is correct or not. Also, any updates on replacing streamreader with filestream would be highly appreciated.

How to control the encoding while serializing and deserializing?

I'm using serialization to a string as follows.
public static string Stringify(this Process self)
{
XmlSerializer serializer = new XmlSerializer(typeof(Process));
using (StringWriter writer = new StringWriter())
{
serializer.Serialize(writer, self,);
return writer.ToString();
}
}
Then, I deserialize using this code. Please note that it's not an actual stringification from above that's used. In our business logic, it makes more sense to serialize a path, hence reading in from said path and creating an object based on the read data.
public static Process Processify(this string self)
{
XmlSerializer serializer = new XmlSerializer(typeof(Process));
using (XmlReader reader = XmlReader.Create(self))
return serializer.Deserialize(reader) as Process;
}
}
This works as supposed to except for a small issue with encoding. The string XML that's produced, contains the addition encoding="utf-16" as an attribute on the base tag (the one that's about XML version, not the actual data).
When I read in, I get an exception because of mismatching encodings. As far I could see, there's no way to specify the encoding for serialization nor deserialization in any of the objects I'm using.
How can I do that?
For now, I'm using a very brute work-around by simply cutting of the excessive junk like so. It's Q&D and I want to remove it.
public static string Stringify(this Process self)
{
XmlSerializer serializer = new XmlSerializer(typeof(Process));
using (StringWriter writer = new StringWriter())
{
serializer.Serialize(writer, self,);
return writer.ToString().Replace(" encoding=\"utf-16\"", "");
}
}

How do I deserialise xml into a DataContract when the XML has had namespaces removed?

I serialise my wcf requests and responses into XML but to save database space, I strip out all non-essential information, so the result is:
<someObject>
<someValue>10</someValue>
</someObject>
There are more complex nested properties, the above is just an example.
When I try to deserialise, I get an error saying expecting someObject, someNamespace but encountered someObject, ''
byte[] data = System.Text.Encoding.UTF8.GetBytes(xmlString);
stream.Write(data, 0, data.Length);
stream.Position = 0;
DataContractSerializer deserializer = new DataContractSerializer(typeof(T));
return deserializer.ReadObject(stream) as T;
Is there an easy way to solve this? Perhaps by not using a DataContractSerializer?
I know this post is almost a week old, but it may help someone if you've already found a solution.
If you know the structure beforehand, you could use the XmlSerializer class.
string xml = "<someObject>" +
" <someValue>10</someValue>" +
"</someObject>";
using (TextReader reader = new StringReader(xml))
{
XmlSerializer serializer = new XmlSerializer(typeof(someObject));
var obj = serializer.Deserialize(reader);
}
Here's the supporting class to deserialize it into:
[Serializable()]
public class someObject
{
[XmlElement("someValue")]
public string someValue { get; set; }
}
For more complex XML, you'd have to modify the class that you're using. You can nest classes as well as support arrays/lists.

Control XML element name dynamically during serialization

here's my problem to be solved: I've a plugin-structure for multimedia shows that allows to implement media types in external assemblies by subclassing from a base class in my framework. A show holds a list of media types. Shows are loaded and saved in XML using the XmlSerializer. This all works, even with programatic type mapping for plugin MediaTypes.
However, I want to be able to load XML files that contain MediaTypes that are not known, because the plugin isn't available.
For illustration, here is such an XML file:
<MultiMediaShow>
<MediaTypes>
<SomeType />
<SomeType />
<AnotherType />
<UnknownTypeFromPluginNotLoaded />
</MediaTypes>
</MultiMediaShow>
In the above example, I assume 2 "known" MediaTypes SomeType and AnotherType, comming from 2 plugin assemblies. The third type (UnknownTypeFromPluginNotLoaded) is unknown.
I'm already able to deserialize such unknown objects, but struggle with the serialization. In my aplication, I've the following code so far:
// To be implemented / subclassed by plugin assembly types
public abstract class MediaType
{
}
public class UnknownMediaType : MediaType
{
[XmlAnyElement]
public XmlElement[] UnknownChildElements;
[XmlAnyAttribute]
public XmlAttibute[] UnknownAttributes;
[XmlIgnore]
public string XmlTagName; // stores the xml element tag name in the original document
}
[XmlRoot("MultimediaShow")]
public class MultimediaShow
{
public List<MediaType> MediaTypes = new List<MediaType>();
}
When deserializing this with XmlSerializer, I use the event UnknownElement and manually insert an UnknownMediaType element into show.MediaTypes:
void HandleUnknownElements(Show show, List<XmlElementEventArgs> unknownElementEvents, XmlAttributeOverrides overrides)
{
// add a root attribute to UnknownMediaType
XmlAttributes attrs = new XmlAttributes();
XmlRootAttribute xmlRoot = new XmlRootAttribute(e.Element.Name);
attrs.XmlRoot = xmlRoot;
XmlAttributeOverrides o = new XmlAttributeOverrides();
o.Add(typeof(UnknownMediaObject), attrs);
// use a new XmlSerializer and a memory stream for deserializting the object as UnknownMediaType.
XmlSerializer xmlSerializer = new XmlSerializer(typeof(UnknownMediaType), o);
using (MemoryStream memoryStream = new MemoryStream())
{
XmlDocument doc = new XmlDocument();
doc.AppendChild(doc.ImportNode(e.Element, true));
doc.Save(memoryStream);
memoryStream.Position = 0;
try
{
// deserialize the object, store the XML element name and insert it into the chapter
UnknownMediaType t = xmlSerializer.Deserialize(memoryStream) as UnknownMediaObject;
t.XmlTagName = e.Element.Name;
show.MediaTypes.Add(t);
}
catch (Exception exc)
{
System.Diagnostics.Debug.WriteLine(exc.ToString());
//return objectType.IsByRef ? null : Activator.CreateInstance(objectType);
}
}
}
The BIG BIG problem is that such an event doesn't seem to be available when serializing an object. What I get as output (not very surpising) is:
<MultiMediaShow>
<MediaTypes>
<SomeType />
<SomeType />
<AnotherType />
<UnknownMediaType /> // !!!! was 'UnknownTypeFromPluginNotLoaded' !!!!
</MediaTypes>
</MultiMediaShow>
However, this is obviously not the same as what I've deserialized. So the question is, how would I best solve this problem?!?!
All help highly appreciated!!
Cheers,
Felix
UPDATE
I was wondering if it is possible to generate classes programmatically that derive from UnknownMediaType and have the class name taken from the UnknownMediaType.XmlTagName. Or, alternativly, to have an attribute for specifying the XML tag name of a class??
Cheers,
Felix
In fact, I implemented some working solution based on building types dynamically. So far, it's doing what I want. The only downside I see at the moment is that I create them into the current app domain, so I can't unload them (e.g. if a new show is loaded or if the plugins would be made available at runtime).
Here's my code:
void HandleUnknownElements(Show show, List<XmlElementEventArgs> unknownElementEvents, XmlAttributeOverrides overrides)
{
foreach (XmlElementEventArgs e in unknownElementEvents)
{
// (1) Dynamically create a type that simply inherits from UnknownMediaType
AssemblyName assName = new AssemblyName("Show Dynamic Type " + e.Element.Name + " Assembly");
AssemblyBuilder assBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.Run);
ModuleBuilder modBuilder = assBuilder.DefineDynamicModule(assBuilder.GetName().Name);
TypeBuilder typeBuilder = modBuilder.DefineType(e.Element.Name, TypeAttributes.Class | TypeAttributes.Public, typeof(UnknownMediaType));
Type objectType = typeBuilder.CreateType();
// (2) Add a root attribute to the type as override
XmlAttributes attrs = new XmlAttributes();
XmlRootAttribute xmlRoot = new XmlRootAttribute(e.Element.Name);
attrs.XmlRoot = xmlRoot;
XmlAttributeOverrides o = new XmlAttributeOverrides();
o.Add(objectType, attrs);
// (3) Use a memory stream for creating a temporary XML document that will be deserialized
using (MemoryStream memoryStream = new MemoryStream())
{
XmlDocument doc = new XmlDocument();
doc.AppendChild(doc.ImportNode(e.Element, true));
doc.Save(memoryStream);
memoryStream.Position = 0;
// (4) Deserialize the object using an XmlSerializer and add it
try
{
XmlSerializer xmlSerializer = new XmlSerializer(objectType, o);
UnknownMediaType t = xmlSerializer.Deserialize(memoryStream) as UnknownMediaType;
show.MediaTypes.Add(t);
}
catch (Exception exc)
{
System.Diagnostics.Debug.WriteLine(exc.ToString());
}
}
}
}
Would be glad if you'd post any issues with this and your concerns...
Cheers,
Felix
See whether you can implement the IXmlSerializable interface for your root class.
From MSDN:
There are two reasons to implement
this interface. The first is to
control how your object is serialized
or deserialized by the XmlSerializer.
For example, you can chunk data into
bytes instead of buffering large data
sets, and also avoid the inflation
that occurs when the data is encoded
using Base64 encoding. To control the
serialization, implement the ReadXml
and WriteXml methods to control the
XmlReader and XmlWriter classes used
to read and write the XML. For an
example of this, see How To: Chunk
Serialized Data.
The second reason is to be able to
control the schema. To enable this,
you must apply the
XmlSchemaProviderAttribute to the
serializable type, and specify the
name of the static member that returns
the schema. See the
XmlSchemaProviderAttribute for an
example.
A class that implements the interface
must have a parameterless constructor.
This is a requirement of the
XmlSerializer class
You can implement a customer serializer. It is more work than using the xmlserializer, but gives you full control. See: http://msdn.microsoft.com/en-us/library/ty01x675(v=vs.80).aspx

XmlSerializer - Encoding not supported

I have the following extension method to serialize my class....
public static string SerializeToXml<T>(this object obj)
{
XDocument doc = new XDocument();
XmlSerializer ser = new XmlSerializer(typeof(T));
using (var writer = doc.CreateWriter())
{
ser.Serialize(writer, obj);
}
return doc.ToString();
}
This seems to work fine and returns the following string for my serialized object:
<AuthenticatedUser xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Username>mark</Username>
<UserID>4</UserID>
<Roles>
<string>AuthenticatedUsers</string>
</Roles>
<IsValid>false</IsValid>
</AuthenticatedUser>
However when I try to deserialize this string using the method below I get this error:
{"The encoding style '<AuthenticatedUser xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\r\n <Username>mark</Username>\r\n <UserID>4</UserID>\r\n <Roles>\r\n <string>AuthenticatedUsers</string>\r\n </Roles>\r\n <IsMale>false</IsMale>\r\n</AuthenticatedUser>' is not valid for this call because this XmlSerializer instance does not support encoding. Use the SoapReflectionImporter to initialize an XmlSerializer that supports encoding."}
Method....
public static T DeserializeFromXml<T>(this string xml)
{
var element = XElement.Parse(xml);
XmlSerializer ser = new XmlSerializer(typeof(T));
using (var reader = element.CreateReader())
{
return (T)ser.Deserialize(reader, xml);
}
}
So after I read the error message I changed the deserialize method to use the SoadReflectionImporter....
public static T DeserializeFromXml<T>(this string xml)
{
var element = XElement.Parse(xml);
SoapReflectionImporter soap = new SoapReflectionImporter();
var mapping = soap.ImportTypeMapping(typeof(T));
XmlSerializer ser = new XmlSerializer(mapping);
using (var reader = element.CreateReader())
{
return (T)ser.Deserialize(reader, xml);
}
}
However I now get this error...
{"The encoding style '<AuthenticatedUser xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\r\n <Username>mark</Username>\r\n <UserID>4</UserID>\r\n <Roles>\r\n <string>AuthenticatedUsers</string>\r\n </Roles>\r\n <IsValid>false</IsValid>\r\n</AuthenticatedUser>' is not valid for this call. Valid values are 'http://schemas.xmlsoap.org/soap/encoding/' for SOAP 1.1 encoding or 'http://www.w3.org/2003/05/soap-encoding' for SOAP 1.2 encoding."}
Does anyone know where I'm going wrong and how I can deserialize this string successfully?
The problem is the overload of the Deserialize method that you are calling:
return (T)ser.Deserialize(reader, xml);
The xml parameter in the call specifies the encoding style, but in this case you are passing the xml from the serialization. Simply delete the second parameter and just call Deserialize with the reader and it should work fine:
return (T)ser.Deserialize(reader);
XElement.CreateReader() doesn't return the XDeclaration.
Instead, try making an XmlReader from a StringReader.
Why are you using XmlSerializer ?
Unless you must control the way the output XML looks, you should be using DataContractSerializer
Here is a nice blog post about the two
Do you need the Parse(xml) call and the reader element? Since you have the string, can't you just deserialize the string? First convert to bytes...
byte [] bytes = Encoding.Unicode.GetBytes(xml);
MemoryStream mem = new MemoryStream(bytes);
returnValue = (T)ser.Deserialize(mem);

Categories