I recently was making an app which needed to store data on the client. The obvious choices were:
- Local file storage (e.g. serialise a class to
XMLand store it in
- Use an
SQLitedatabase to store the data and read/write when needed
I have done both before, and generally find that the
XML storage is a quick and easy method of storing data on the client app - however it has it’s problems…
Why XmlSerialization is good
As stated above,
XmlSerialization is quick and easy to implement.
Mono (.Net) has out of the box support for
System.IO to read and write files, so it’s very simple to create multiple files to store your data in and be done with it.
XmlSerialization is a perfectly natural, acceptable and encouraged way of saving files to file system. In fact,
XmlSerialization works fine for simple data storage where objects are individual, and belong to their parent object, and do not need to be referenced by any other object.
Here is a simple helper class I knocked up in 30 seconds:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
Consider the following simple forum domain model™:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Topic contains an
List<Post>, in Domain language
Topic.Posts - logical, huh?
XmlSerialize that model, and we will get a nice
XML file which looks like:
<?xml version="1.0" encoding="utf-8"?> <Topic xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Subject>My first topic</Subject> <Posts> <Post> <Body>Hello world!</Body> </Post> </Posts> </Topic>
OK. So we have a nice
XML file which is easy to read and write to, easy to pass around, easy to code. And we have a nice little helper method to use to save this file.
What’s wrong with XmlSerialization
As I said before, nothing is wrong with
XmlSerialization. Use it when the problem you need to solve is solvable by using it, but understand the drawbacks (a very non-committal tongue twister for you).
XmlSerialization has a series of pre-requisite rules which must be met in order to serialize a file. I’m not going to go into them all, but if you are interested here is the MSDN article.
For me, the big ones have been:
- Properties must be implementations, not interfaces. e.g.
- Relationships are not easily supported (without extra attributes)
Consider the following, more realistic domain model™:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
It’s virtually the same as before, except it’s slightly more realistic:
- There is a new class,
Topiccontains a new property
Postcontains a new property
What we now have is a relational data structure.
Using the same
FileHelper let’s save the model to an
XML file, which results in:
<?xml version="1.0" encoding="utf-8"?> <Topic xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Subject>My first topic</Subject> <Owner> <Username>Macropus</Username> </Owner> <Posts> <Post> <Owner> <Username>Macropus</Username> </Owner> <Body>Hello world!</Body> </Post> </Posts> </Topic>
At first glance, it looks all ok. But it’s not…
Users are not the same instantiation
Even though the .Net code uses the same instance (reference) of
User, when the
XmlSerializer serializes the data to the
XML file, an element
<Owner> is created in both
This will result in the deserialization process instantiating 2 different
User objects, which do not share the same reference.
You can get around this by writing some logic that stores only the User ID, and peppering your domain objects with business logic - but that’s bad practise.
Performance and Disk usage
Secondly - Imagine the size of the
XML files (and the time to serialize and deserialize) if you had 1,000 topics, each with 100 posts and just 1 user…
(I originally tried 10,000 topics, but got bored of waiting)
iOS Simulator on my MacBook:
- Serialize and Write: 1671 milliseconds, filesize 15 megabytes
- Deserialize and Read: 4679 milliseconds
- Serialize and Write: 34400 milliseconds, filesize 15 megabytes
- Deserialize and Read: 83712 milliseconds
So, to extend our example further - we would now like to be able to reference all of the
Post objects, and we express this in our Domain Model as:
1 2 3 4 5 6 7 8 9 10 11 12
Now if we try and serialize our model:
System.InvalidOperationException: There was an error generating the XML document. ---> System.InvalidOperationException: A circular reference was detected while serializing an object of type User.
We have a circular reference, which
XmlSerialization has no clue what to do with:
There is no easy way to get around this using
XmlSerialization without creating
Id properties on your classes, and creating a pseudo-relational database in
[XmlIgnore] on your Domain Model. Again this is bad practise on a domain model (I have a thing for keeping my Domain Models ‘pure’).
To retain transparency, you could use a Binary Serializer to mitigate… But that’s not the title of this post.
BinarySerialization solves this problem but breaks when you start using events (it serializes your events which results in a large part of the heap put into the binary). You can’t tell it to ignore events, instead, you have to play a game with events as fields - which takes about 8 lines of boilerplate per event.
.NET serialization seems to have been developed with the idea of message passing and that’s it. Serializing graphs is a miserable experience.
So, what was all of this about? Well… I was rambling. This post was originally meant to be a comparison of
SQLite and using
XmlSerialization to store data in
XML files on your iOS device.
Instead, it’s a dissection of the reasons why I use
XmlSerialization in some projects, and not in others - the comparison will have to wait for another post in the near future!
For what it’s worth, (and just to confuse) in my latest app (due to be released soon) I use both of these methods to store data, and files… More to come!
As always, here is the source