Dimitre Novatchev (father of XSLT functional programming) writes:
The fact that XLINQ is as it is reflects that it targets a completely different audience, who would never attempt to learn XPath :o)There is some truth in his words. But I don't want to be "completely different audience" actually, I like XLinq and I do believe it can make XML programming easier for masses. While tragetting innocent developers who are too
You are a person, who doesn't really need XLINQ and the authors of XLINQ had totally different audience in mind.
Most of your remarks in the remaining "Bitter words" also reflect this fact.
Taking the fact of the completely different audience targeted by XLINQ, many of the "shortcomings" for an experienced XML professional will actually be regarded as useful features for innocent OOP-ers.
The point is, LINQ is a general purpose language for querying and transforming data, so of course there will be cases where a domain-specific query language (like xslt) will be more powerful, more compact, or more expressive. Our hope though, is that for those common cases where you don't need the extra power, this will be (as one PDC-goer put it) 'the last weird query language you'll ever have to learn.'Well, the recipe is known:
Simple things should be simple. Complex things should be possible.
I personally just love this feature. Expanded name is a core XML notion that exists since early days of XML. Obviously XLinq didn't invented it, XPath, XML Namespaces, XSLT and XQuery all use expanded name as an abstraction for an XML name. Where XLinq innovates is providing concrete syntax representation for an expanded name. In XML in general and XQuery particular expanded name has no syntax and you probably can guess that's not just a whim. There are some issues, you know.
First - it's all plain strings - no syntax checking help from C# compiler, you going to be informed about invalid expanded name only at runtime. Curiously enough at the moment XLinq isn't smart enough to detect that there is something wrong in "new XElement("{dd}d}foo")". This compiles and even runs ok :)
Those numerous string concatenations/validations/tokenizings...
Every other XML API and XML itself uses prefixes. Leaving them out sure is too dramatical step and will confuse lots of not-so-advanced-in-XML developers. Curretly XLinq seems to be way to liberal, down to being no-namespace aware - you can create an element p:foo with no namespace declared for prefix "p" and XLinq won't complain. You customer would though.
Unfortunately I don't believe you can ignore prefixes completely, despite QNames in content considered harmful, they are ubiquitous. So XLinq have to clutter API provide some facilities to work with QNames and prefixes like you it or not. What about "prefix{ns-name}localname" triple syntax form? :)
Simplify is a good mantra though.
Here is how it works now:
XElement book = new XElement("foo", new XElement("bar", "baz")); Console.WriteLine(book.ToString()); Console.WriteLine(book.Xml); Console.WriteLine((string)book);The result:
<foo> <bar>baz</bar> </foo> <foo><bar>baz</bar></foo> bazActually I can live with it, but what do you think?
Another confusing thing is XElement.SetElement() method. Setting an element to a magic null value means removing the element. So this method either sets element's value or removes it depending on value provided. Hmmm, weird. That reminds me early C functions which used to be doing many different things depending on magic argument values. Are we back to realloc()-like design?
That might be really good idea actually. And at the same time such design has drawbacks. Main one you can see immediately by looking at XLinq API - those nasty object and object[] all around the methods. Yes, lots and lots of methods accept and return anything or arrays of anything. That's the price XLinq pays for sacrifycing text and attribute nodes. What's wrong with object[] based API? It's loosly typed and no compile time checks. You are suposed to read API documentation to figure out what you should pass to a method or what you would get back. That's not really a good idea. I'm sure developers would try to put DataSets into XElement constructor and then wonder why it doesn't come back. It's object, so you can pass *anything* and when you get it back it's your responsibility to figure out what do you get. Hence type switches all around the code. OOP developer in me cries "That's wrong!", but may be I'm wrong and XLinq indeed means "anything"? XLinq seems to be escaping to object[] in API because that's the only way to say "XNode, XAttribute and String" (actually that might change once XLinq becomes strongly typed). And the reason why to escape is obviously lack of attribute and text nodes.
Btw, I have no idea why XDocument constructor accepts object[] and not XNode[]. After all XDocument can only contain XDocumentType, XDeclaration, XElement, XCommment or XProcessingInstruction and all they inherit XNode. So currently "new XDocument("hmmm");" compiles well, but crashes at runtime. Why not to catch it at compile time??
I'm sure loosly typed nature of XLinq API hides more such "ooops" moments.
Honestly speaking I'm not sure if it worth it. What's so wrong with attributes and text nodes so one would screw up his API just to avoid them? Unless you go nuts (just like W3C DOM) by allowing adjacent text nodes, text nodes within attributes or entity references or similar crazyness, you are safe. XPath and now XQuery (like every other XML Tree API) have text nodes and I never heard of any problems with them (well, except for whitespace-only text nodes). Not to mention that at the same time XQuery supports strongly typed elements with no any troubles. Really, what's the reason for not having attributes and text as nodes in XLinq?
I'm packing to the MVP summit, so sorry for messy thoughts.
XML Tree functional construction is a great stuff and definitely a big improvement over the upside down DOM-style XML building. I only wanted to note that one doesn't have to wait years for C# 3.0 to be able to build XML tree in a natural top-down way, that was actually possible from the very beginning (here is .NET 2.0 sample, in .NET 1.X one would need XmlNodeWriter):
XmlDocument doc = new XmlDocument(); XmlWriter w = doc.CreateNavigator().AppendChild(); w.WriteStartElement("contacts"); w.WriteStartElement("contact"); w.WriteElementString("name", "Patrick Hines"); w.WriteStartElement("phone"); w.WriteAttributeString("type", "home"); w.WriteString("206-555-0144"); w.WriteEndElement(); w.WriteStartElement("phone"); w.WriteAttributeString("type", "work"); w.WriteString("425-555-0145"); w.WriteEndElement(); w.WriteStartElement("address"); w.WriteElementString("street1", "123 Main St"); w.WriteElementString("city", "Mercer Island"); w.WriteElementString("state", "WA"); w.WriteElementString("postal", "68042"); w.WriteEndElement(); w.WriteEndElement(); w.WriteEndElement(); w.Close();Should admit XLinq's functional construction still looks shorter and tree-friendly, but it is seriously constrained to building only XLinq XML tree in-memory. If one day you decide that building in-memory XML tree just to be saved to a disk is a waste of memory (and it is a waste), you would need to rewrite completely XML construction code and that's bad. XmlWriter has no limits here - you can use it for building XML tree in memory or writing XML directly to a stream in a fast efficicent non-caching way. XmlWriter just does its job - writes XML with no coupling to the result and that's way any code that produces XML with XmlWriter is more reusable.
Ergo - please don't repeat System.Xml mistakes and treat XmlWriter as a first class citizen - provide a way to build XDocument/XElement with XmlWriter, that will be good architecturally and will provide smoother migration from XmlDocument v2. E.g. what about providing an editable XPathNavigator over XElement or XDocument?
Update. And as a matter of interest XQuery being truly functional language of course supports functional composition too. Here is a sample:
element book { attribute isbn {"isbn-0060229357" }, element title { "Harold and the Purple Crayon"}, element author { element first { "Crockett" }, element last {"Johnson" } } }Looks terribly familiar, huh?
But as Erik pointed out in comments neither form of composition can beat the ultimate way of building XML - literal one. Here is how it looks like in XQuery:
<example> <p> Here is a query. </p> <eg> $b/title </eg> <p> Here is the result of the query. </p> <eg>{ $b/title }</eg> </example>C# doesn't support it yet, while VB sort of does. "Sort of" because VB form of "literal XML" is not actually XML, but confusing ASP-like mess. But I'll address that later.
To be continued...
I hope Microsoft XML Team would consider adding support for xml:id into the next .NET version and into XLinq. That's really valuable addition to the XML Core.
And of course I cannot avoid Canonical XML (C14N) and xml:id controversy. Read Norm for the crux of the issue. In short - Canonical XML is broken. Here is an illustration:
string xml = @" <foo xml:id=""f42"" xml:base=""http://foo.com""> <bar xml:base=""dir"">baz </foo>"; XmlDocument doc = new XmlDocument(); doc.LoadXml(xml); XmlDsigC14NTransform c14n = new XmlDsigC14NTransform(); c14n.LoadInput(doc.SelectNodes("/foo/bar")); StreamReader sr = new StreamReader((Stream)c14n.GetOutput()); Console.WriteLine(sr.ReadToEnd());Guess what is the result?
<bar xml:base="dir" xml:id="f42"></bar>Not only xml:id is inherited by bar element (so now you can get different element when searching by the same ID), but xml:base is broken (absolute base URI part is lost). Too bad. Canonical XML should definitely be fixed.