Why is XmlSerializer so hard to use? - c#

I imagine to use XML serialization like this:
class Foo {
public Foo (string name) {
Name1 = name;
Name2 = name;
}
[XmlInclude]
public string Name1 { get; private set; }
[XmlInclude]
private string Name2;
}
StreamWriter wr = new StreamWriter("path.xml");
new XmlSerializer<Foo>().Serialize (wr, new Foo ("me"));
Edit: I know this code is wrong. It was just to display how I would like to use it.
But this does not work at all:
XmlSerializer is not generic. I have to cast from and to object on (de)serialization.
Every property has to be fully public. Why aren't we just using Reflection to access private setters?
Private fields cannot be serialized. I'd like to decorate private fields with an attribute to have XmlSerializer include them.
Did I miss something and XmlSerializer is actually offering the described possibilities? Are there alternate serializers to XML that handle these cases more sophisticatedly?
If not: We're in 2010 after all, and .NET has been around for many years. XML serialization is often used, totally standard and should be really easy to perform. Or is my understanding possibly wrong and XML serialization ought not to expose the described features for a good reason?
Edit: Legacy is not a good reason imo. Listwas nongeneric at first, too.
(Feel free to adjust caption or tags. If this should be CW, please just drop a note.)

See XmlSerializer class. You'll see you're using it wrong. XmlInclude has a totally different purpose.
You're right. The XML Serializer has been around since .NET 1.0. That's before we had generics, BTW, so it's unlikely to support them.
Also, better technologies have arrived since then:
The DataContractSerializer is faster, and supports serializing as binary
LINQ to XML can be used in many serialization scenarios, and is much more flexible
The XML Serializer is unlikely to be enhanced in the future. I recommend you learn the other alternatives.

First the fixed code, then the answers to your questions:
public class Foo {
public Foo() : this("") {}
public Foo (string name) {
Name1 = name;
Name2 = name;
}
// note only this will be serialized
public string Name1 { get; private set; }
// this won't
private string Name2;
}
or in 3.0:
[DataContract]
class Foo {
public Foo (string name) {
Name1 = name;
Name2 = name;
}
[DataMember]
public string Name1 { get; private set; }
[DataMember]
private string Name2;
}
(and use DataContractSerializer instead of XmlSerializer)
XmlSerializer is not generic. I have to cast from and to object on (de)serialization.
That is common for serializers. I have my own serializer, and initially I did make it fully generic. And it turned out to be a big design mistake. Huge. No, seriously. I'm currently in the process of re-writing every line of code to switch it out.
Simply; serializers generally involve some level of reflection (either for code-gen or for the actual work, depending on the implementation). Reflection and generics don't play nicely, especially on some of the frameworks like WCF. Having your code do the final cast is a fair compromise. I have a number of blog entries on this if you really want...
Every property has to be fully public.
That is indeed a limitation of XmlSerializer (although a list/colletion without a setter is fine, it will throw if you have a public get and private set). Also, the type needs to be public and have a parameterless constructor.
Why aren't we just using Reflection to access private setters?
For performance, XmlSerializer builds an assembly on the fly to do what you want. It doesn't have automatic access to your code's internals. For info, I'm doing something similar but I offer 2 levels of generation; fully static (into a deployable dll), which then only works with public members, or in-memory, which can still access private members. I guess they wanted to settle on only 1 model, which makes sense - and they needed "sgen", which dictates the first model.
Private fields cannot be serialized. I'd like to decorate private fields with an attribute to have XmlSerializer include them.
Then use DataContractSerializer, which will serialize any member (including private) marked [DataMember].

1: legacy. XML Serializer predates generics. It as in with .NET 1.0.
2: Design decision. XML serializer is supposed to work with very limtied rights, compared to other solutions.
3: same as 2.
You can use WCF DataContract serializer in parts.
Your assumption is "limited wrong". XML serialization is supposedly for transfer documents, which - in my projects - are always separate classes doing nothing more. As such, I have no problem with all the limitations.

You don't need the
[XmlInclude]
Like you have it. You can use
[XmlElement]
[XmlAttribute]
...
To describe the way the class is serialized.
Take out the [XmlInclude] and see if that works.
class Foo {
public Foo (string name) {
Name1 = name;
Name2 = name;
}
[XmlAttribute]
public string Name1 { get; set; }
[XmlAttribute]
public string Name2;
}
Foo myFoo = new Foo("FirstName", "LastName");
StreamWriter wr = new StreamWriter("path.xml");
XmlSerializer serializer = new XmlSerializer(typeof(Foo));
serializer.Serialize(wr, myFoo);
Updated, the serialized properties should be public.

By the way DataContract never supports binary serialization , it serializes to xml but supports binary encoding.

Related

C# How do you solve a circular object reference

I've run into what i belive could be a major issue for my code design and i was hoping someone here could explain to me how i would work around the issue.
I have 2 classes which each have a property of the other class creating a circular reference. I plan on serializing these classes and using XSLT to format the output but i'm assuming this will fail due to the circular reference.
Example
public class Book
{
public BookShop TheShop = new BookShop();
}
public class BookShop
{
list<Book> Books = new list<Book>();
}
So from this example each book will be in a bookShop and each bookshop will have many books. If i serialize the bookshop it will then serialize each book which then serialize a bookshop and so on round and round. How should i handle this?
Tag TheShop with an attribute to prevent its serialization.
[XmlIgnore] with the default serializer.
http://www.codeproject.com/KB/XML/GameCatalog.aspx
Probably just a problem with your example, not your real code: Don't use public fields but properties. I think XmlSerializer doesn't even serialize public fields.
Add [XmlIgnore] to the TheShop property to prevent it from being serialized.
You can then set it manually when deserializing.
Best practice would be to have the BookShop class implement an interface (IBookShop) and then have the Book class store the interface not the concrete class. You should also make BookShop into a property in the Book class:
public class Book
{
public Book(IBookShop bookShop)
{
TheStop = bookShop;
}
[XmlIgnore]
public IBookShop TheShop { get; set; }
}
public interface IBookShop
{
void SomeMethod();
}
public class BookShop : IBookShop
{
list<Book> Books = new list<Book>();
public void SomeMethod()
{
}
}
If you're going to use System.Xml.Serialization.XmlSerializer, you should decorate TheShop with System.Xml.Serialization.XmlIgnoreAttribute:
public class Book
{
[System.Xml.Serialization.XmlIgnore]
public BookShop TheShop;
}
That is, assuming the BookShop is the root object you wish to serialize. MSDN
First you need to check whether this is really a problem. If you always care about a bookshop when you have a book, and you always care about all the books a bookshop has, then it's perfectly sensible to have the whole graph serialised. This doesn't result in an infinite loop, because the serialisation uses an identifier to indicate a reference to an object already serialised (there is a bug if you do an XML serialisation of a graph with a circular reference in its types, but that's a bug rather than inherent to the problem of serialising XML, as the fact that it can be resolved proves, see Why do I get a "System.StackOverflowException was unhandled " exception when serializing? on that).
So, maybe you don't want to do anything here at all, and you're fine as you are.
Otherwise, the question is - just what do you want to serialise? Most suggestions so far have been to not serialise the TheShop property. This could be fine, or it may be useless if you will need to later access that shop.
If you have some sort of identifier (id number, uri) for each shop, then you could perhaps memoise - access to TheShop looks first at whether a private _theShop is null, and if it is, loads the relevant object into _theShop based on that identifier. Then you just need to serialise the identifier, not the full object.
Finally, if you are using XSLT to format the output to some other specification (whether XHTML for display, or something else) you may find it simpler just to roll your own XML serialisation. While this is a more complicated task in many ways, the fact that the XML produced by serialisation isn't particularly convenient for reformatting for display may mean that overall it's simpler this way. Indeed, if this is your only reason for serialising (you will never deserialise from the XML produced) then it may be much easier, as you need only consider what the XML for display needs, and not worry about anything else. Hence serialising may not be the best approach at all, but simply a ToXml() method, or a WriteBookToXml() method in another class.

C# - what attributes to use to support serializing using both XMLSerializer and DataContractSerializer?

I have some simple POCO object:
public class ProductCategoryDTO
{
public string Name { get; set; }
public DateTime ModifiedDate { get; set; }
}
As sometimes field order is important (for example, if sending to Infopath forms), I need to keep element order when serializing.
And now I am confused, what attributes I should use for the class and for each field. I know that:
DataContractSerializer uses [DataContract] and [DataMember(Order = n)]
XMLSerializer uses [Serializable] and [XmlElementAttribute(Order = n)].
Then what attributes to use if I want to support both XMLSerializer and DataContractSerializer, so it can used in both WCF or ASP. web services?
Strictly speaking, you don't need to use any attributes for either ;-p It used to be that DataContractSerializer would demand [DataContract] / [DataMember] (and they absolutely should be used), but you can use it without (but it then acts in a very dubious way similar to BinaryFormatter). Likewise, XmlSerializer doesn't need anything unless you want to control things. There are, however, some differences you should note:
XmlSerializer demands (and uses) a public parameterless constructor; DataContractSerializer doesn't use a constructor (at all). So watch out for that, and don't (for WCF) rely on code in the ctor - if you have necessary init code, use a serialization callback for WCF.
XmlSerializer demands either public fields (yeuch) or public properties with both get and set (even for lists); DataContractSerializer will happily work against private members, properties with (for example) a public get and private set, and collections without a `set (as long as your type initialises it).
XmlSerializer demands public types; IIRC DataContractSerializer is less fussy
So yes; you can support both serializers, and you can add any number of attributes in parallel, but note the above if you want total compatibility.
Another option is to just use XmlSerializer; you can configure WCF to use XmlSerializer by using [XmlSerialzerFormat]. Both options support inheritance, via [XmlInclude] and [KnownType].
Finally, note that if you implement IXmlSerializable, this takes precedence over either, but it hard to get right. Don't do that unless you have to.
I don't see any reason why you couldn't put both attributes on the class and member properties, if you really must. Doesn't look nice, but if it works for you, that's just fine!
[DataContract(Namespace="....")]
[XmlType]
public class ProductCategoryDTO
{
[DataMember(Order=1)]
[XmlElementAttribute(Order=1)]
public string Name { get; set; }
[DataMember(Order=2)]
[XmlElementAttribute(Order=2)]
public DateTime ModifiedDate { get; set; }
}
Order of XML elements should be dictated by the WSDL and you don't need to worry about it. Starting from .NET 3.5 SP1 you no longer need to use DataContractAttribute and DataMemberAttribute. The serializer will automatically include all public properties. As far as XmlSerializer is concerned, the SerializableAttribute has no effect. This attribute is used for binary serialization by the BinaryFormatter. So to resume, you could leave the class as a POCO, expose it either in WCF or ASP.NET webservice and leave the clients consume it according to the WSDL.

Using XmlSerializer with private and public const properties

What's the simplest way to get XmlSerializer to also serialize private and "public const" properties of a class or struct? Right not all it will output for me is things that are only public. Making it private or adding const is causing the values to not be serialized.
XmlSerializer only looks at public fields and properties. If you need more control, you can implement IXmlSerializable and serialize whatever you would like. Of course, serializing a constant doesn't make much sense since you can't deserialize to a constant.
Even though it's not possible to serialize private properties, you can serialize properties with an internal setter, like this one :
public string Foo { get; internal set; }
To do that, you need to pre-generate the serialization assembly with sgen.exe, and declare this assembly as friend :
[assembly:InternalsVisibleTo("MyAssembly.XmlSerializers")]
Check out DataContractSerializer, introduced in .NET 3.0. It also uses XML format, and in many ways, it is better than XmlSerializer, including dealing with private data.
See http://www.danrigsby.com/blog/index.php/2008/03/07/xmlserializer-vs-datacontractserializer-serialization-in-wcf/ for a full comparison.
If you only have .NET 2.0, there's the BinarySerializer that can deal with private data, but of course it's a binary format.
It doesn't make sense to consider const members, as they aren't per-instance; but if you just mean non-public instance members: consider DataContractSerializer (.NET 3.0) - this is similar to XmlSerializer, but can serialize non-public properties (although it is "opt in").
See here for more.
One other solution the use of Newtonsoft.Json:
var json = Newtonsoft.Json.JsonConvert.SerializeObject(new { root = result });
var xml = (XmlDocument)Newtonsoft.Json.JsonConvert.DeserializeXmlNode(json);
Sure, this one has unfortunately detour via json.
Here's my solution to putting immutable values in a property that will serialize to XML:
[XmlElement]
public string format { get { return "Acme Order Detail XML v3.4.5"; } set { } }

Is there a way to do object (with its attributes) serializing to xml?

Create a class (call it FormElement). That class should have some properties like the metadata they have with data elements (name, sequence number, value—which is just a string, etc).
This class has as attributes of type Validation Application Block Validation classes.
I want to serialize it to xml and deserialize it. Verify that all properties of the class including the validation application block attributes survive serialization.
some suggestion?
The .NET framework has this built in, using C# you would do it like this:
// This code serializes a class instance to an XML file:
XmlSerializer xs = new XmlSerializer(typeof(objectToSerialize));
using (TextWriter writer = new StreamWriter(xmlFileName))
{
xs.Serialize(writer, InstanceOfObjectToSerialize);
}
And this snippet is an example of how to deserialize an XML file back to a class instance:
// this code creates a class instance from the file we just made:
objectToSerialize newObject;
XmlSerializer xs = new XmlSerializer(typeof(objectToSerialize));
using (TextReader reader = new StreamReader(xmlFileName))
{
newObject = (ObjectToSerialize) xs.Deserialize(reader);
}
You must mark your class with the [Serializable] attribute for these to work. If you want to make your XML output a little more pretty, you can use [XmlElement] and [XmlAttribute] attributes on your class properties to have them serialize into your schema of choice.
By saying serialize, do you mean use the official Serialization mechanism, or achieve a similar effect?
If your objects are beans, you could use reflection to write a general service that takes a class and writes down its class name and properties. It can similarly read materials from the XML and generate the object (which is what Apache Digester essentially does).
What Jonathon Holland said.
However, you also asked about validation. If you use the code Jonathan posted, all of your properties will serialize and de-serialize correctly. But if you really want to check it, there is a property you can set with your XmlSerializer object for a *.xsd schema to validate against. You can create the schema easily enough from your class by using the xsd.exe command-line tool that is included with Visual Studio.
Also, it sounds like you might want to control whether certain properties of your class are serialized as attributes or elements. Jonathon touched on that, but I want to show an example:
[Serializable]
public class FormElement
{
[XmlAttribute]
public string Name {get; set;};
[XmlAttribute]
public int Sequence {get; set;};
[XmlAttribute]
public string Value {get; set;};
[XmlElement]
public Validation OnValidate{get; set;}
[NonSerialized]
public string UnimportantProperty {get; set;};
}
Finally, the type for every property that you want to serialize must also be serializable, and complicated types must be serialized as XmlElements. Otherwise you won't be able to do it.
XStream is a pretty good java library for doing just that.

Serialization of struct objects by webservices

I have 'extended' the System.DateTime struct by adding some essential fields to it. Ideally I'd like to be able to deliver this object via a webservice to a winforms client.
I've marked the stuct type as [Serializable] and it also implments ISerializable, however if I inspect the XML being delivered by the webservice it simply contains an empty tag for the object.
Putting breakpoints all over the place has lead me to believe that when the object gets de-hydrated the ISerializable method void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) never appears to get called.
There are various reasons why I'd prefer to keep this as a struct, but will convert it to a class if necessary.
Does anyone know why GetObjectData is being ignored by the .net framework while it is preparing the data for the webservice response? The struct which I am working with contains a DateTime member and a few booleans.
please note, this is .net 2.0!
Cheers
First, web-services use XmlSerializer - so you'd need IXmlSerializable for custom serialization. The standard XmlSerializer serialization only acts on public properties that have both a getter and setter.
Second, structs generally don't work very well as web-service DTO objects; in particular, XmlSerializer demands things be mutable... which structs shouldn't be.
I'd use a class, personally. If you can give more info, I might be able to say more...
For example:
[Serializable]
public class FunkyTime
{
[XmlAttribute]
public DateTime When { get; set; }
[XmlAttribute]
public bool IsStart { get; set; }
[XmlAttribute]
public bool IsEnd { get; set; }
}
(note you can tweak the xml layout / names in various ways)

Categories