Developer Blog Search

Thursday 15 May 2008

What do you think of my code formatter?

you can see it in action in my last post, at the moment it doesn't work properly in IE, the regex engine seems to be doing something differently!

It's really simple to use unlike some, where you feed it your code and it'll do everything and return you a big block of html. It is all dynamic and the code looks like this:

<pre class="codeblock sh_csharp" title="title of code">
using mycode.code;

public class myclass {

}
</pre>

it highlights (using SHJS, so basically any language you can think of can be highlighted) and makes it look pretty, lets you show/hide it, show/hide line numbers and copy the code to the clipboard.

are there any other features anybody would like to see?

I'm going to release the code under the GNU GPL. Once it's finished and works in IE properly!

IE local won't run show my page so I can't test it. I've been uploading and testing on here, so forgive me as syntax highlighting only currently works on the first code block at the moment and my lunch break is over! :)

Monday 12 May 2008

Xml serialization of objects with circular references

I always felt this was missing from XmlSerializer.

Then like magic, along came DataContracts, which can be used to do roughly the same thing, except you now have the added option of preserveObjectReferences as a parameter of the constructor.

Great, this solves the problem right? well, yes but it also raises more :) the resulting xml for a simple structure like this:

public class Site { Page[] pages; string name; } public class Page { Site site; string name; }

This looks more complex because it uses reference attributes. Every serialized object is given an ID attribute. If that object is serialized again (i.e. from a circular reference) it will just use an attribute that says "i'm really object 54".

<a:Site z:Id="1" xmlns="urn:test" xmlns:a="http://schemas.datacontract.org/2004/07/EmitTest" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"> <a:Name z:Id="3">Site 1</a:Name> <a:Pages z:Id="4" z:Size="4"> <a:Page z:Id="5"> <a:Name z:Id="6">Page 1</a:Name> <a:Site z:Ref="1" i:nil="true" /> </a:Page> <a:Page z:Id="8"> <a:Name z:Id="9">Page 2</a:Name> <a:Site z:Ref="1" i:nil="true" /> </a:Page> <a:Page z:Id="11"> <a:Name z:Id="12">Page 3</a:Name> <a:Site z:Ref="1" i:nil="true" /> </a:Page> <a:Page z:Id="14"> <a:Name z:Id="15">Page 4</a:Name> <a:Site z:Ref="1" i:nil="true" /> </a:Page> </a:Pages> </a:Site>

This makes it rather difficult to do XSLT and XPath with the result. So unless you don't mind resolving every node in a query, I've written implementations of XPathNavigator and XmlDocument to transparently resolve these references for you.

using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml.XPath; using System.Reflection; using System.Xml; namespace EmitTest { public class DataContractXPathNavigator : XPathNavigator, IHasXmlNode { private XPathNavigator _nav; private XmlDocument _doc; public DataContractXPathNavigator(XPathNavigator nav, XmlDocument doc) { _nav = nav; _doc = doc; } public override string BaseURI { get { return _nav.BaseURI; } } public override XPathNavigator Clone() { return new DataContractXPathNavigator(_nav.Clone(), _doc); } public override bool IsEmptyElement { get { return _nav.IsEmptyElement; } } public override bool IsSamePosition(XPathNavigator other) { return _nav.IsSamePosition(other); } public override string LocalName { get { return _nav.LocalName; } } public override bool MoveTo(XPathNavigator other) { bool result = _nav.MoveTo(other); ResolveReference(); return result; } public override bool MoveToFirstAttribute() { return _nav.MoveToFirstAttribute(); } public override bool MoveToFirstChild() { bool result = _nav.MoveToFirstChild(); ResolveReference(); return result; } public override bool MoveToFirstNamespace(XPathNamespaceScope namespaceScope) { return _nav.MoveToFirstNamespace(namespaceScope); } public override bool MoveToId(string id) { return _nav.MoveToId(id); } public override bool MoveToNext() { bool result = _nav.MoveToNext(); ResolveReference(); return result; } public override bool MoveToNextAttribute() { return _nav.MoveToNextAttribute(); } public override bool MoveToNextNamespace(XPathNamespaceScope namespaceScope) { return _nav.MoveToNextNamespace(namespaceScope); } public override bool MoveToParent() { return _nav.MoveToParent(); } public override bool MoveToPrevious() { bool result = _nav.MoveToPrevious(); ResolveReference(); return result; } public override string Name { get { return _nav.Name; } } public override System.Xml.XmlNameTable NameTable { get { return _nav.NameTable; } } public override string NamespaceURI { get { return _nav.NamespaceURI; } } public override XPathNodeType NodeType { get { return _nav.NodeType; } } public override string Prefix { get { return _nav.Prefix; } } public override string Value { get { return _nav.Value; } } private void ResolveReference() { try { string refattr = _nav.GetAttribute("Ref", "http://schemas.microsoft.com/2003/10/Serialization/"); if (refattr != String.Empty) { XPathNavigator refnode = _nav.SelectSingleNode("//*[@" + _nav.LookupPrefix("http://schemas.microsoft.com/2003/10/Serialization/") + ":Id='" + refattr + "']", this); if (refnode != null) { MoveTo(refnode); } else { throw new InvalidReferenceException(refattr); } } } catch (StackOverflowException ex) { throw new CircularTemplateReferenceException(_nav.Name); } } #region IHasXmlNode Members public XmlNode GetNode() { return ((IHasXmlNode)_nav).GetNode(); } #endregion } public class InvalidReferenceException : Exception { public InvalidReferenceException(string referenceId) : base("Unable to resolve reference " + referenceId + ".") { } } public class CircularTemplateReferenceException : Exception { public CircularTemplateReferenceException(string nodeName) : base("A circular reference was detected on node " + nodeName + ".") { } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml; using System.IO; using System.Runtime.Serialization; namespace EmitTest { public class DataContractXmlDocument : XmlDocument { private XmlNamespaceManager _nsm = null; public DataContractXmlDocument(object root) { DataContractSerializer dcs = new DataContractSerializer(typeof(Site), "a:" + root.GetType().Name, "", null, 200, true, true, null); MemoryStream ms = new MemoryStream(); dcs.WriteObject(ms, root); LoadXml(Encoding.UTF8.GetString(ms.ToArray())); } public override System.Xml.XPath.XPathNavigator CreateNavigator() { return new DataContractXPathNavigator(base.CreateNavigator(), this); } public virtual new XmlNode SelectSingleNode(string xpath) { if (_nsm == null) { _nsm = new XmlNamespaceManager(NameTable); foreach (XmlAttribute attr in base.SelectSingleNode("/*").Attributes) { if (attr.Prefix == "xmlns") { _nsm.AddNamespace(attr.LocalName, attr.Value); } } } return base.SelectSingleNode(xpath, _nsm); } } }

This now makes it possible to do the following with your serialized objects:

Console.WriteLine(doc.SelectSingleNode("/a:Site/a:Pages/a:Page[1]/a:Site/a:Name").InnerXml);

remember though, this also allows you to get your xslt in an infinite loop easily. be careful.

LINQ to SQL bug?

hmm, I have only noticed this in Web Developer Express Edition but it may be present in other versions.

Basically, it happens when you are mapping a field in one of your classes to an enum (using the LINQ to SQL editor).

  1. add a table with an int field
  2. change int field to enum name
  3. save

this works fine! now I go in and change something in the LINQ to SQL file - pretty much anything from what I can tell, add something, change a property, etc.

once done I rebuild and get a validation error for the dbml file. if I change the mapping back to int, save, change back to my enum, save, it works again. until the next time I have to change something on the model! rather annoying bug!

that is the only way around it I know of. Anyone who knows more about this than me please comment because it's driving me crazy!

First blog post

Hi, this is my first blog post, will I actually use this blog? only time will tell.. I do hope so though.