Wednesday, September 30, 2009

Useful, Popular And Commonly Required MSDN Pages

Just thought a collection of these would be good. I'll try to keep it updated...

Tuesday, September 22, 2009

Monday, September 21, 2009

Properly Handling CDATA In .NET XSLT

Prologue:

I have a T-SQL table of data, one field of which contains escaped X/HTML - that is, it has XHTML where the important characters, like < and >, are represented as &lt; and &gt; etc. Some of this XHTML is broken because the writer was probably using a rich-text editor which sent fragments of XHTML to the database. Of course, using SQLXML to pull this out I find that the body of some of the elements contains this escaped HTML. So now I need to unescape it back into normal HTML, so it can be rendered on a page without hassle, but without making it part of the XML document.

Sumary:
Ok, so, in summary, we have valid XML with escaped XML (or XHTML) which needs to be wrapped sensibly so it's not mixed up and we need to do this in .NET.

First problem:

Unescaping the code, in XSLT, is a little tricky, but if you assume every element which needs unescaping is the same, then we can do this:

<xsl:template match="Description">
<xsl:copy>
<xsl:value-of select="node()" disable-output-escaping="yes" />
</xsl:copy>
</xsl:template>

What this does is match every element named 'Description' in the original XML and unescape it's content - so code like &lt; and &gt; become < and >. Simple. If you want to do that for specific elements only, just move the 'value-of' XSL element to where you want it.

Second problem:

We now have XML containing some more XML, which should really be considered plain text, until it's needed. So, we want to keep this 'inner XML' as it is (ie: pretty XML and not escaped) but indicate that it's not part of the current XML document.

Second solution:

Wrap the body content of the elements we want to remain as body content in a CDATA tag, like this:

<xsl:template match="Description">
<xsl:copy>
<![CDATA[<xsl:value-of select="node()" disable-output-escaping="yes" />]]>
</xsl:copy>
</xsl:template>



What the CDATA block does is tell any parsers that the content is to be, essentially, ignored. Simple.

Unfortunately, the CDATA block tells the parser not to parse the content within it, so what we actually end up with is:

<Description>
<![CDATA[
<xsl:value-of select="node()" disable-output-escaping="yes" />]]>
</Description>

Third problem:

If you just add the CDATA block to your XSLT a standard parser will take the XSL element within it as to be ignored, and not process it.

Third solution:

The answer to this is not to include the CDATA block ourselves, but to tell the XSLT document which elements need to contain the CDATA block, instead. Thus, the XSLT parser will wrap them itself. We do this by adding the 'cdata-section-elements' attribute to the output element of the XSLT document:

<xsl:output method="xml" indent="yes" cdata-section-elements="Description" omit-xml-declaration="yes" />

Now, every <Description> element, in the output XML, will contain a CDATA block which wraps the body content of the element, like this:

<Description>
<![CDATA[
<strong>Some content that was parsed from the value-of XSL element.</strong>]]>
</Description>

However, if you have been trying this as we go along and seeing something different - ie, it's not working for you - that's because, in .NET, you have to handle XSLT in a certain way.

The short answer is, here's the C# to do it:

/// <summary>
/// Takes a string containing XML to be parsed and a string containing the the XSLT to do the parsing.
/// </summary>
public string NewTransform(string xml, string xsl)
{
// create the readers for the xml and xsl
XmlReader reader = XmlReader.Create(new StringReader(xsl));
XmlReader input = XmlReader.Create(new StringReader(xml));

// create the xsl transformer
XslCompiledTransform t = new XslCompiledTransform(true);
t.Load(reader);

// create the writer which will output the transformed xml
StringBuilder sb = new StringBuilder();
XmlWriter results = XmlWriter.Create(new StringWriter(sb), t.OutputSettings);

// write the transformed xml out to a stringbuilder
t.Transform(input, null, results);

// return the transformed xml
return sb.ToString();
}

/// <summary>
/// Takes an absolute path to a file which is then loaded into a string and returned.
/// </summary>
public string LoadFileAsString(string fullpathtofile)
{
using (var sr = new StreamReader(fullpathtofile))
return sr.ReadToEnd();
}

The key to ensuring that the cdata-section-elements attribute, and therefore the CDATA block, is handled correctly is the t.OutputSettings - passing the settings to the writer informs it of the intentions of the XSL transformer.

Epilogue:

I battled with all of the above until I stumbled upon this post, so thank you to everyone there:

Thursday, September 17, 2009

Augmented Reality On The iPhone

Well, I've finally found something cool, useful and right here, right now...
I found this via the original link...
Now, signing up (free, easy and quick) to the iPhone app Yelp, gives you something very useful: Switch on Monocle (shaking the iPhone isn't necessary any more - I discovered too late!) and it'll show you which restaurants, bars, etc are in the direction your phone is facing. It's that easy to find places. Tapping the screen takes you to the contact information and from there the world is yours!

Yes, I'll say it again, I'm in love with the iPhone. Well done Apple - and all the enterprising developers out there making the best use of a very good gadget!

Space Photos On A Budget

Recently, some MIT students took their own photos of the Earth's curve...
Further reading, mostly derived from the above links, but the Apollo stories are some of Mankind's most important, heroic and touching...

Wednesday, September 16, 2009

Make Sure Your IIS Uses The Correct .NET Version - Unrecognized attribute 'type'

Recently, I created a new .NET 3.5 web site in VS2008 and started getting this error:
  • Parser Error Message: Unrecognized attribute 'type'.
This page helped me here:
Essentially, the problem is that IIS must be configured to use the correct version of .NET to run the site. Therefore, I opened the IIS panel "properties" for my site, clicked the ASP.NET tab and switched the version from .NET 1 to 2 and everything was fine.

Point IIS to .NET 2 even for .NET 3.5 web apps.

So, yes, the issue was I was using the wrong version of .NET to run my website. D'oh!

Lifehacker Fun

LifeHacker fun...

Wednesday, September 09, 2009

iPhone Problems And Solutions

Well, I love the iPhone, but like any device it's not perfect. Ok, I've not found anything that isn't pretty much perfect (built by Apple, at least) but having found my first problem, hopefully here's some solutions, some generally useful links follow...
  • Unsent email won't send. A sent email fails to appear in the "Sent" folder and the message "1 unsent email" displays at the bottom of the mail application.
    For me, this resolved itself, I assume by finally managing to send. Here is a useful link related to the issue:
    http://support.apple.com/kb/TS1426?viewlocale=en_US
Generally useful links to be aware of:

Sunday, September 06, 2009

Loving Up The iPhone

Finally made the switch from a dying T-Mobile Windows Mobile MDA Vario II, to the 16GB iPhone 3GS and it's awesome! This thing completely surpasses all expectations in performance, usability, speed and sheer number of applications available - there are far more applications than for Windows Mobile and they are so easy to get. The addictive nature of downloading to the iPhone, I think, would compare to the discovery of P2P file sharing.

Anyway, I was disappointed when I discovered (yes, lots of this is new to me) that I can't just dump files onto the iPhone like I can with the iPod (using it as an external HD, for example) or that ringtones cannot be selected from any music on the device. I did, though, find a very good page with instructions on how to do this. Originally written, then tidied up, for the iPhone 3G, this page is accurate for 3GS as the method is fairly generic;
In short, it goes like this:
  1. Get an audio file and edit to around 20-30 seconds
  2. Save as MPEG-4 (.m4a - see WavePad for easy converting)
  3. Change file extension to .m4r
  4. Drop into the Ringtones section of iTunes
  5. Sync Ringtones to your iPhone
The ringtones will now be available in the Sounds section of you iPhone's settings. Easy. There is more detail on the above link and a pointer to the http://audiko.net/ site for even easier conversions, but manual conversion was my preference (and is really very easy.)

WavePad is available here:
On the other side of the coin, we have videos for the iPhone. As with iPod and iPod Touch, there are plenty of videos to purchase or stream (iPhone comes with a YouTube app) but what if you have a load of wmv's or avi's you want to just dump there? I would get the iPod/iPhone video converter Videora because it has all the "want that one" simple functions that you could need, while being fast and producing quality output. Then it's simply a case of syncing your iPhone to your videos (videora can auto-add conversions to iTunes' library or you can manually drop them onto the phone) to make them play on the iPhone.
Red Kawa, the makers of Videora, even have easy-to-use apps for making wallpapers for the iPhone, but with the wallpaper selector built in, I don't think those are necessary.

Tuesday, September 01, 2009