I've got my fingers crossed that someone out there has a good understanding of the Microsoft XmlSerializer.
Basically, I have built a system for outputting XML on the back of the serializer which is now causing me a lot of hassle and I am trying to avoid a significant re-write.
Introduction
My current system employs a lot of derived classes.
For example,
BaseResponse -> CarParkResponse -> CarParkResponseInherited
BaseResponse and CarParkResponse (both in a library dll) are seperate from CarParkResponseInherited.
As a result, the base classes are ignorent of the derived class.
What I have done
-> Declared derived classes
I appreciate that in order for xmlserializer to do its thing, there are a number of ways to declare dervied classes.
Declare derived classes in base classes using XmlInclude
or
Declared types to be serialised and deserialised in xmlSerialiser. For example:
XmlSerializer serializer = new XmlSerializer(typeof(CarParkResponse), new Type[] { typeof(CarParkParameters), typeof(CarParkInformation) });
I have gone with the latter.
What I am trying to do
Because the derived classes are seperate and they share the same properties, what I am trying to do is serialize the dervied class as the subclass.
In other words deserialize CarParkResponseInherited as a CarParkResponse.
In "CarParkResponseInherited" I am using the declaration XmlTypeAttribute("CarParkResponse").
Initially this causes a problem when serialized
Types 'CarParkResponseInherited' and 'CarParkResponse' both use the XML type name, 'CarParkResponse', from namespace ''. Use XML attributes to specify a unique XML name and/or namespace for the type."
as the type is already in use "CarParkResponse" so I've declased XmlType("Response") in "CarParkResponse" to get around this.
The Problem
I am now trying to Deserialize the XML returned but I'm now getting the following problem.
The specified type was not recognized: name='CarParkResponse', namespace='', at OriginalRequest xmlns=''
Frustratingly the CarParkResponse is there. I am wondering if this is down to the fact I've used XmlType("Response") in the class and the serializer doesn't recognise the class as being type of CarParkResponse
So my question is
Basically is it possible to pass a derived class as a sub class and have it deserialized as the subclass using the XmlSerializer?
I have experimented with the xml produced.
For example, if I don't use the Serialization tags (such as XmlType() etc.) anywhere in my code. The xml produced will produce a carparkresponse element with "type" as an attribute.
The value is "CarParkResponseInherited". I have written some code that removes "Inherited" from the value which does allow it to be deserialized.
This approach is obviously not good so I am hoping there is a good way around this.
So...
if there is anyone out there that has had to patience to read through this post and can help or offer advice it would be most appreciated as I am all out of ideas.
Thanks.
It might be possible to do something using the XmlAttributeOverrides class. It's mostly used for specific property overrides, so it may or may not be the best thing for you.
The long way around would be to go ahead and serialize as the inherited class. Then, if the mappings are the same, deserialize the xml into the base class, and then, re-serialize into what you want.
Depending on how much or how little control you have over the assemblies, you could override the serialization on the inherited class so it does it as the base class.
Related
I have a class generated by Linq2Sql:
public partial class BuyerOrder : INotifyPropertyChanging, INotifyPropertyChanged
I want to clone object of this class, like it's done in this post. For this purpose I define the partial class in not generated file, which I mark as serializable:
[Serializable]
public partial class BuyerOrder
But when I'm calling
formatter.Serialize(stream, source);
I'm getting an exception, saying that this class is not marked as serializable. What am I doing wrong?
If you want to serialize a LINQ-to-SQL type, then tell the code-gen to emit serializable data. You can do this in the DBML, or more simply in the designer - just set the serialization mode to unidirectional (this is the #Serialization attribute on the root <Database> element in the DBML).
This will generate attribute markers suitable for use with DataContractSerializer; LINQ-to-SQL is designed to be serializable with DataContractSerializer. It is not designed to be serializable with BinaryFormatter.
Every class derived from BuyerOrder must also be decorated as [Serializable], as well as all objects that the serializing instance holds a reference to (unless decorated as NonSerializable).
The exception should tell you the type that is missing the serializable attribute. If you can not or do not want to decorate all the classes you will need to get a little more creative.
-- The other possibility --
One option is to use the technique described in Implementing a generic deep-clone for C# objects. Since this can be done entirely in memory and without a binary formatter it will perform many times faster than serialization based cloning.
The source code is located at http://csharptest.net/browse/src/Library/Cloning
It only takes two lines of code:
using (ObjectCloner cloner = new SerializerClone())
fooCopy = cloner.Clone(foo);
I have around 50 classes in polymorphic, inheritance relation. I used XmlSerializer to serialize them. Now for few classes I want to implement IXmlSerializable. So I started for one of class.This class not base class but derived directly from base class of all polymorphic relation.
After implementation, resultant XML contains tags only for above class which I have written in WriteXML method. There is no information for other classes. Very strange behavior!!!!
So please guide me to solve this issue, so that other classes information would maintain.
As I understand you correct - your xml contains properties that you implicit add in WriteXML (of current class)? But it is correct behavior. You should implicit add item as in example in description of interface.
If you need properties of base classes - add them. if you need properties of classes that inherit from this class - try to get them via reflection. Get Property Names using Reflection . But instead of typeof(MyClass) use .GetType() and check there how to get properties values.
Similar questions here and there.
End the ReadXml(XmlReader reader) method with the line
reader.Read();
seams to solves the problem...
I have a custom class that only has one property of type string.
Attempting to serialise an object of this class as an attribute using XmlSerializer results in the exception:
XmlAttribute/XmlText cannot be used to encode complex types
Now I know I can fudge this my creating a pseudo string property in my containing type and serialising that instead, but is there any way at all to make my custom class support it intrinsically?
How do .Net classes do it (eg DateTime)?
In order to this you have to implement IXmlSerializable and fully control how your type is serialized / deserialized. Rather overwhelming effort for such an easy task, though. People mostly get stuck with this when it comes to Nullable types.
Is there a way in .Net 4 to easily deserialize a stream of mixed objects one by one? I can read to the start of the element for an object I want to deserialize using XmlTextReader.Read(), but have tried many ways to deserialize that specific object unsuccessfully.
The types I want to deserialize can be read as a list of those types without a problem using XmlSerializer, however I want to be able to mix them rather than having input files containing just lists of one object type.
e.g.
<Objects>
<TypeA>...</TypeA>
<TypeB>...</TypeB>
<TypeA>...</TypeA>
<TypeC>...</TypeC>
...
</Objects>
The ordering of the objects in would be random.
Many thanks for any pointers.
I've looked at XmlSerializer, DataContractSerializer and XElement, but could not get them to work for this (although I possibly didn't set those up correctly as I'm not very familiar with them).
You can do that with the XmlSerializer.
However, be careful with the following:
The array you are serializing/deserializing must be declared as an array of "object" (or the base object if all other types inherit from it)
Each type will have "xsi:type" attached to it
You must use [XmlInclude] to include all the type(s) you are ever going to need with the "root" object.
The need to [XmlInclude] all the object types mean that you're not going to be able to dynamically add types to the serialization. You'll need to add [XmlInclude]'s and recompile to include the new type(s).
Your XML, however, will become:
<Objects>
<TypeObj xsi:type="TypeA">...</TypeObj>
<TypeObj xsi:type="TypeB">...</TypeObj>
<TypeObj xsi:type="TypeA">...</TypeObj>
<TypeObj xsi:type="TypeC">...</TypeObj>
:
</Objects>
This is the most flexible and "normal" way of approach XML serialization of multiple types. However, if you need to keep your exact format, you can declare your class this way:
[XmlRoot("Objects")]
public class Objects
{
[XmlElement("TypeA")] public TypeA[] TypeAObjects;
[XmlElement("TypeB")] public TypeB[] TypeBObjects;
[XmlElement("TypeC")] public TypeC[] TypeCObjects;
:
}
[XmlElement] means that all the objects are jumbled up on the same level (different from XmlArray). They do not even have to be in order.
The pitfalls of doing this, however, is that if you want to add a new type, you'll have to modify the "Objects" class.
Not sure if this is helpful, but it might be an idea to take a look at how RestSharp does their deserialization. https://github.com/johnsheehan/RestSharp
Specifilcally Take a look at RestSharp/Deserializers/XmlDeserializer.cs
You would need to create an XmlSerializer for each type. How many types you have and how many times each would get used would determine if it's better to create a new XmlSerializer for each object as you process it, or store them in a Dictionary<string, XmlSerializer> to be reused. The XmlSerializer takes the type in its constructor, and then you can call the Deserialize method, passing it a StringReader that contains the XML string you read from your file. Hopefully that's enough to get you started, but if you need more help I can throw together some sample code ;)
Are there any closed or open source projects for a XML serializer for C# that can serialize for the most part any object without the need to pollute my domain objects with tons of attributes? That will also handle serialization of collections built with the internal generics classes? A bonus would be that it can handle serializing an interface type property. Another bonus would be that it can serialize objects that have read-only properties (or atleast with the get accessor marked internal)
Well, first define "advanced", i.e. what specifically do you need that XmlSerializer doesn't have. In terms of POCO, XmlSerializer has an overloaded ctor that accepts all the attributes you could ever want to add, to avoid having to add them to your object model - but it does still require a public parameterless constructor, and only works on public read/write fields/properties. And you should cache/re-use the serializer if you use this approach.
I'm not aware of any like alternatives, simply because in most cases this is "good enough" - and it is often a mistake to try to brute-force your existing domain object into a DTO. It may be simpler and more maintainable to simply map your domain entities onto a new DTO(s) that are attributed (and have appropriate ctor/properties/etc).
Note that for the ctor/properties issue DataContractSerializer has some answers, but this doesn't have as much fine-grained control over what the xml looks like.
You can allow System.Xml.dll to access your internals by using the InternalsVisibleToAttribute.
Thus serializing internal types and/or internal members. Including internal .ctors.
You can also implement IXmlSerializable on classes to customize their serialization (like the container containing interface references).
You do not have to provide the XML serialization attributes on your classes, but provide them as XmlAttributeOverrides instead.
XmlSerializer is almost always exactly what people want, they just don't know that it is as flexible as it really is.