August 10, 2006
If you need to store a little bit of state-- in your configuration file, or on disk-- nothing is faster than some quick and dirty serialization. Or as I like to call it, stringization.
In late 2004, I wrote about The Last Configuration Section Handler, which does exactly this for *.config files. It's based on earlier work by Craig Andera of Pluralsight. Let's bring that code up to date for Visual Studio 2005, and furthermore, we'll do it in C# and The Language of the Gods, VB.NET.
The first thing to do is set up a little class that represents the data you want to serialize. Include whatever types you need, but make everything public so it'll be visible to the serializer.
public class MyStuff
public int i;
public string s;
Now use this routine to serialize it:
static string SerializeObject(object o)
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
XmlTextWriter xtw = new XmlTextWriter(sw);
xtw.Formatting = Formatting.Indented;
XmlSerializerNamespaces xsn = new XmlSerializerNamespaces();
XmlSerializer xs = new XmlSerializer(o.GetType());
xs.Serialize(xtw, o, xsn);
string s = sb.ToString();
// <Foo> becomes <Foo type="MyClass.Foo">
s = Regex.Replace(s, "(<" + o.GetType().Name + ")(>)", "$1 type=\"" + o.GetType().FullName + "\"$2");
The output is your class, serialized as a nice human-readable string.
<s>A bunch of information</s>
It's just so darn.. straightforward. As if I needed another reason to love strings. Anyway, take that string and paste it into your web.config file.
To read it in, you'll need a custom config section. Paste this into your config file to define one:
<section name="MyStuff" type="XmlSerializerSectionHandler, CSSerializerSection" />
The actual XmlSerializerSectionHandler is a bit too much code to paste into a blog post, but it's still relatively simple:
- Extract the type from the XML Type attribute
- Make sure the type is valid
- Deserialize the XML into a new object of that type
The XmlSerializerSectionHandler is too verbose to reprint here mainly because I added a bunch of error trapping. If something goes wrong, you get a nice explanatory exception instead of a cryptic error. It's good stuff.
There's almost no difference at all between the two languages, except that VB for some reason requires an additional namespace; instead of "SomeNamespace.MyStuff", it's "VBSerializerSection.SomeNamespace.MyStuff".
Posted by Jeff Atwood
"The Language of the Gods, VB.NET"
Oh no you di'int.
Here is an intensive course of serialization with Ruby. Note that the XML serialization could be further improved. :-)
a = %w(aaaa bbbbbb ccccccccccc) # Array
s = Marshal.dump(a)
a_copy = Marshal.load(s)
xml = a.to_xml.to_s
a_from_xml = Array.from_xml(REXML::Document.new(xml).root)
yaml = a.to_yaml
a_from_yaml = YAML.load(yaml)
o = Person.new
o.name = 'Joe'
s = Marshal.dump(o)
o_copy = Marshal.load(s)
xml = o.to_xml.to_s
o_from_xml = Person.from_xml(REXML::Document.new(xml).root)
yaml = o.to_yaml
o_from_yaml = YAML.load(yaml)
VB.NET has something called a root namespace, which is different from the C# default namespace. Whenever a root namespace is defined (in project properties) it's added behind your back.
I have no idea why anyone would want this, or why it is that way. It's strange to have something added invisibly in a language which force us to write code about the size of a book just to get a read-only property.
Tish and pish, how about in the language of the proletariat, PHP:
$string=serialize( $my_array );
I still think those public fields just look wrong wrong wroooong...
If you try to serialize objects this way, you're going to run into security problems. The app.config file is stored in the same folder as the executable which is usually somewhere in the Progrm Files folder. Standard users do not have write access to Progrm Files.
Here's a good... uh, arguable (dodging bullets?) metric to fan some flames in the CS v. VB war:
I challenge your Language of the Gods with my Language of the Verbose.
I think all ideas which produce discussion are great.
NB: I would use generics - we will gain some performance and make code look more elegant.
You must also consider that the class might have overriden the root element name for the class (XmlRoot).
Therefore, add this code:
// Foo becomes Foo type="MyClass.Foo"
Type t = o.GetType();
string name = t.Name;
object attributes = t.GetCustomAttributes(typeof(XmlRootAttribute), false);
if (attributes != null attributes.Length 0)
XmlRootAttribute xmlRoot = attributes as XmlRootAttribute;
name = xmlRoot.ElementName;
s = Regex.Replace(s, "(" + name + ")()", "$1 type=\"" + t.FullName + "\"$2");
You have to be crazy to use regex's for Xml Serialization... here it is in four lines:
public string Serialize(object obj)
using(StringWriter sw = new MemoryStream())
XmlSerializer xs = new XmlSerializer(obj.GetType());
If you want to change the name of fields or objects in the XML you simply apply attributes to them:
public class MyStuff
public int i;
public string s;
The reason why PHP serializes more simply is because XML is only one type of serialization available to .NET you can also (for instance) serialize to a byte or to JSON by using the above code with simply using different formatters. Also you should check out Windows Communication Foundation and how to make a DataContract... very cool stuff.
Still brilliant; I can't count how many times I've used this. Also, I believe you may use:
New XmlSerializer(t, New XmlRootAttribute(section.Name))
...to remove the dependancy of the element name to class name (even if root is renamed or redeclared or attribute sprinkled - thanx 4 the hint Michael P) .
(You may also consider encoding the xml values to UTF8 first)
Thanks Jeff, Craig, Michael Jef N.
PS: I'm also posting this here so I don't forget :)
VB.NET may not be the language of the Gods, but it's certainly the language of love. There, I said it.
Yeah, I reference the root namespace every time I have to summon up an embedded resource. Big deal.
VB.NET is the Language Of Love!!!
Allow me to reiterate. Assembly (and, to a lesser extent, C and its' variants) is the language of love for the computer. It affords every convience for the lowest level, for the compiler, and makes people bend to its' will. Similarly, the LOL (Language of Love, VB.NET), bends over backwards to make itself readable to humans. This means that is is easier for LOL (VB.NET) programmers to write code that is more self-documenting. To write code which resembles the syntax of the English lanuage. VB.NET is the language of love for the developer!
The true virtue of any language these days is not speed or brevity. It's readability and maintainability. VB.NET leverages the same framework as C#, but in a way that allows the developer to name things in such a way that his code is self explanatory. And that's worth a thousand semicolons and two thousand curly braces.
And that's love for the person typing in the code. And the friend that's reading it.
.NET 2.0's config classes/attributes are great for basic persistence. Not so great for deep-nested data structures or for producing truly human-readable XML, and then there's the security issue.
Using proxy classes as in this post is generally a good way to tackle the issue because they are easy to serialize. It's also pretty much the only way to transport data over a Web Service in a form readable by non-.NET apps. One could also write a small persistence class library, if one were so inclined; 2.0's Generics make life a lot easier in that respect.
As for the VB.NET sub-discussion, simply because the syntax is closer to plain English than C# does not make it any more readable or maintainable. Verbosity may facilitate understanding for some but is a distraction to others. And I'm not a C# evangelist - my primary language is Delphi, which is similar to VB in a lot of ways, but I "love" all of those languages equally because I am "fluent" in all of them, and I think that if people would bother to learn other tools beyond 10-minute tutorials then they might be a little less stuck-up about the tool of their choice. Ruby and Python, for example, give orders of magnitude more "love" to developers than any of the .NET languages, in some areas at least.