Daniel Cazzulino's Blog :

XML (RSS)

Why XAML makes System.Configuration and Enterprise Library Configuration obsolete

It may seem unrelated, but if you haven't read Fowler's article on DSLs (from Google cache if it's down like now), please do so now. It will help you understand why XAML goes far beyond WPF and presentation.

Configuration is typically nothing more than a DSL (external DSL in Fowler's terms) that serves to setup the variable parts of a certain runtime library or framework. For example, while a logging framework has many "static" or invariant components (i.e. logging "pipeline" execution, interaction between components, registration of variable components, etc.), there are variable parts which determine the runtime behavior, such as concrete trace listeners attached to given trace sources, the specific file a file listener will write to, and so on.

With careful API design, manipulating the actual runtime classes wouldn't be significantly different than manipulating its corresponding configuration classes or files. Your API is already a sort of DSL for your domain. For example, system.diagnostics can contain the definition of trace sources:

<source name="TraceTest" switchName="SourceSwitch" switchType="System.Diagnostics.SourceSwitch" >
  <listeners>
    <add name="console" />
    <remove name ="Default" />
  </listeners>
</source>

To achieve the same using the corresponding runtime classes, you would write:

				TraceSource source = newTraceSource("TraceTest");
source.Switch = newSourceSwitch("SourceSwitch");
source.Listeners.Add(newConsoleTraceListener());
source.Listeners.Remove("Default");

Note how close the two are. All you need is a good object (de)serialization format and you can go straight from the external DSL ("config" file) to the runtime objects without any intermediate representation. This wasn't possible before with .NET 1.x and 2.0 due to the lack of extensibility of the XmlSerializer and its excesive (IMO wrong) focus as a generic XML Schema <-> object converter (pretty much an unattainable goal if you want full fidelity both ways) and a webservices-only feature.

XAML, on the other hand, was thought from the beginning as a fully extensible object serialization format. With it, it's quite easy to achieve what's shown above, straight from the domain/runtime classes. One possible XAML syntax for the TraceSource example could be:

<TraceSource Name="TraceTest">
  <TraceSource.Switch>
    <SourceSwitch Name="SourceSwitch"/>
  </TraceSource.Switch>
  <TraceSource.Listeners>
    <ConsoleTraceListener />
  </TraceSource.Listeners>
</TraceSource>

(this wouldn't work with current diagnostics classes)

You end up having executable models :)

The only requirement to enable XAML serialization on your classes is to provide a public parameterless constructor, and public read/write properties for the stuff you want to get serialized. Validation can be done after deserialization by implementing System.ComponentModel.ISupportInitialize.

So, why would you want to develop and maintain a DSL/configuration model for your domain classes ever again?

And XAML is a v1 release, so I can only expect it to get better, more flexible and powerful over time. 

posted Monday, September 24, 2007 9:00 PM by kzu with 2 Comments

How to cross post entries across blogs from Windows Live Writer

I think I'm not the first guy to end up in this situation: I created a lot of content (and loyal readers) on some blog hosting site (in my case http://weblogs.asp.net) and now I want to move on, maybe with a self-hosted solution, maybe with Blogger and its custom domain support, whatever.

There are two separate issues:

  1. You don't want to start on your new site with zero content. You have created a TON of it on the old site. This batch migration process can be painful and you may need to write code to do it. Typically you'll want to leave on the old site just a "teaser" of your entries, and a link to your new cool site. At the very least, you want to leave a link.

    At this point I hope you realize that the "why not an HTTP 301 and that's it?" is not a valid answer for pretty much any blog hosting site I know of. They won't do that kind of redirect if you want to move off, and there's still the question of compatibility of URLs generated by your new blogging engine, etc. I'd keep it "simple" and add a link to the old entries automatically using some blogging API your engine must surely expose.
  2. You don't want to lose the traffic that the old (but surely much better established) community site used to generate. On my current blog at Clarius, more than half the traffic is still coming from http://weblogs.asp.net. I only started tracking this recently through Google Analytics (post for another day) so it may very well have been much higher percentage when I had just moved.

    Also, once you have moved to the new site, in order to keep the old one bringing traffic, you may want to cross-post (either full entries or summaries/teasers) to the old one, so that people start coming to your new blog to read the entries and leave comments.

You might be thinking: in order to just post the same content in two different blogs, all I need to do is write my entry, Publish to one blog account in WLW, then switch to another blog account, and post again. True, but that way you have basically forked your traffic and your audience: now users will be posting comments on both entries, oblivious to the fact that there's one new location that you'd rather have them use. Also, because your old blog has been around for more time and in an established community, those are the entries that will more likely be linked by others and shown first on search engine results, etc. All that will go against your goal of moving people over to your new site.

So what I did to solve issue 2) is create a WLW plugin that allows me to cross-post blog entries (summaries or full posts, at my choice) that link back to my main/new blog, all with a single click:

image

And I provide some plugin options too:

image

image

The options deserve some more explanation besides being quite obvious:

  • Target blog: this is a dropdown that shows all the blog accounts you have currently registered in WLW. The "source" blog will always be the one you're editing and publishing the current blog entry to.
  • Post summary: if checked, the cross-post will only contain the first 500 (200 is the default) characters of your current entry.
  • Preview post: this option (true by default) allows you to see in WLW the generated summary or full entry with back-link, and it will show up with the target blog selected in WLW, as well as a new Draft that you can save, publish, delete, etc. Once you get more confident with how the plugin works, you'll typically want to unckeck this option, as it's pretty annoying to end up with duplicate titles in your "Recently Posted" sidebar for every entry.

My plugin is leveraging all of WLW features for posting, so if you can post to a blog from WLW, you can cross-post to it with my plugin too.

With the options above, here's what I got for one of my recent entries on PowerShell:

image

The plugin will automatically add the "..." when the characters limit is reached, append a link with an icon pointint to the full article in your source blog, and also a full link at the bottom. It's interesting to note that I'm not doing just a string.Substring there. That would either break the HTML pretty bad, or I'd have to post only the text content value of the HTML, which would break code blocks, styles, etc. as it would not include any markup. Instead, what I'm doing is the "right thing" by having an internal custom XmlReader that counts the characters it reads from text nodes and starts reporting stops reading further (but does report end elements for all ones from the current location). In the case above, you can see that the character count was exceeded while in the middle of some code (typically a <pre> element), yet the image link is displayed correctly, as well as the full link.

Installation is trivial. From the project release page, you can either:

  • Get the MSI installer. It will just copy the plugin dll to your plugins folder under the WLW install location (will look for %ProgramFiles%\Windows Live Writer\Plugins)
  • Get the binaries. Copy the included files to the above location manually.
  • Get the sources. Open the solution in VS2005. Build. An AfterBuild target will attempt to copy the output to the above location, so you should even be able to run/debug from there.

 

I submitted the plugin to the Windows Live Gallery, so it may show up there too :)

 

Enjoy!

posted Friday, August 24, 2007 10:23 AM by kzu with 3 Comments

Reading XML document fragments in .NET

A document fragment is an XML file that doesn't have a root element:

<E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent">
  <System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system">
... </System> <ApplicationData>
...
</ApplicationData> </E2ETraceEvent> <E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent"> <System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system">
...
</System> <ApplicationData>
...
</ApplicationData> </E2ETraceEvent> ...

 

In order to read this with plain XmlReader code, you can do the following:

XmlReaderSettings settings = new XmlReaderSettings();
settings.ConformanceLevel = ConformanceLevel.Fragment;

using (XmlReader reader = XmlReader.Create("tracelog.xml", settings))
{
    while (reader.Read())
    {
        // Process each node of the fragment, 
        // possibly using reader.ReadSubtree()
    }
}

It may be the case that instead of the more performant (and definitely suggested approach if the file is big) XmlReader API, you may want to use XmlDocument or XPathDocument to be able to query the file using full XPath. In .NET 2.0, the XPathDocument class will successfully load an xml fragment, which was kind of surprising to me as I'm pretty sure it was not a supported feature in .NET 1.1. The surprising part is because technically, a fragment is NOT a document. The way the XPathDocument does its magic is by creating a "transparent" root node, and holding the fragments from it. I say it's transparent because your XPath queries can use the root node axis and still get properly resolved to the fragments:

private void LoadXPath(object sender, EventArgs e)
{
    string xml = @"<item id='1' /><item id='2'/>";

    XmlReaderSettings set = new XmlReaderSettings();
    set.ConformanceLevel = ConformanceLevel.Fragment;

    XPathDocument doc = new XPathDocument(
        XmlReader.Create(new StringReader(xml), set));

    XPathNavigator nav = doc.CreateNavigator();

    Debug.Assert(nav.Select("/item").Count == 2);
}

 

Note how the query is "/item", which would return the single root element in a regular XML document, but here it returns the two items which are hanging from the "transparent" root (that's why I call it such, as there's no way to "see" it, not even by using the OuterXml on an XPathNavigator sitting on the root). 

If you're more of an XmlDocument guy, or maybe you need to perform updates to the fragments using random access via XPath queries too, then you're pretty much out of luck, as the XmlDocument will fails with a "System.Xml.XmlException: There are multiple root elements. Line x, position y." exception if you try to load an xml fragment (via string or XmlReader, doesn't matter).

Here's where a new class we added to the Mvp.Xml library can help: XmlFragmentReader. This is an XmlReader-derived class which receives a root element name (and optionally a namespace) to fake a root node. Because it's an XmlReader class, it does so in a streaming fashion, and shouldn't have an impact in reading performance, as all its members just pass-through to the base reader (the one reading the fragments) except on the initial state and the ending one (where the fake root is reported). Usage is straighforward:

private void LoadXml(object sender, EventArgs e)
{
    string xml = @"<item /><item/>";

    XmlDocument doc = new XmlDocument();

    XmlReaderSettings set = new XmlReaderSettings();
    set.ConformanceLevel = ConformanceLevel.Fragment;

    using (XmlReader reader = new XmlFragmentReader("root",
        XmlReader.Create(new StringReader(xml), set)))
    {
        doc.Load(reader);                
    }

    Debug.Assert(doc.SelectNodes("/root/item").Count == 2);
}

Note  that the root node is not transparent as is the case with the XPathDocument, so you have to include it in your XPath queries.

Get the Mvp.Xml library and enjoy! 

posted Friday, August 03, 2007 5:28 PM by kzu with 0 Comments

QNames in attribute values considered useful

Quite some time ago (around XML raise as a universal data format), the W3C seemed to be in doubt of the value of QNames (prefix + local name) in attribute values.

One very clean evidence of this inconsistency around the best practice on QNames on attribute values is the W3C XML Schema specification, which was pretty happy with QNames in attributes:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema targetNamespace="my-schema"
              xmlns="my-schema"
              xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="element1" type="xs:string" />
</xs:schema>

 

At the same time and in the complete opposite direction, the RDF specification, developed around the same time as XML itself, clearly favors XML entities instead of allowing QNames on attributes:

<?xml version="1.0"?>
<!DOCTYPE rdf:RDF [<!ENTITY xsd "http://www.w3.org/2001/XMLSchema#">]>

<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
         xmlns:exterms="http://www.example.org/terms/">

  <rdf:Description rdf:about="http://www.example.org/index.html">
    <exterms:creation-date rdf:datatype="&xsd;date">1999-08-16</exterms:creation-date>
  </rdf:Description>

</rdf:RDF>

(from the RDF Primer)

 

RDF explicitly forces the rdfs:datatype and other attributes to be full URI References, which is why the only abbreviation mechanism is through XML entities. I find it hard to see why the above is any better than:

<?xml version="1.0"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:exterms="http://www.example.org/terms/"> <rdf:Description rdf:about="http://www.example.org/index.html"> <exterms:creation-date rdf:datatype="xsd:date">1999-08-16</exterms:creation-date> </rdf:Description> </rdf:RDF>

 

Given that XML Schema, SOAP, even XAML heavly uses QNames in attributes, I believe RDF is now in an awkward position maitaining that annoying restriction. It would be as simple to solve as saying "when a QName is used, it will be expanded using the XML prefix mapping in scope, plus the fragment identifier '#' plus the local name", so that xsd:date becomes http://www.w3.org/2001/XMLSchema#date.

posted Monday, July 16, 2007 7:21 AM by kzu with 1 Comments

Open source implementation of Simple Sharing Extensions (SSE) is available now!

SSE is an XML micro-format and corresponding sync algorithm that can be embedded in RSS or Atom feeds to allow for two-way synchronization among peers. More important: the algorithm allows for mesh-style synchronization between nodes, with no concept of a "master" copy. This can be game-changing for data-exchange and app-level data interop. But only the future will tell.

SSE was initially announced by