Recently, the three of these technologies came into my head all at once, and for a moment they looked strikingly similar.
Here's why.
Each is an application of XML.
Each constrains the use of XML constructs by imposing an underlying (or overarching) data model on how elements and attributes relate.
Though the syntax for the three differs wildly, the underlying data model each of them is more alike than it is different.
All three support the notion of named properties.
In XAML, it's every attribute and every element whose name begins with the parent element's name + "." (e.g., <Button.Font/>).
In SOAP/1.2, it's every child element of an element marked s:nodeType="struct."
In RDF, it's whatever representation is used for a predicate (see the examples below).
All three support the notion of sequences of values.
In XAML, it’s every child element whose name doesn't begin with the parent's element name + '.'.
In SOAP/1.2, its every child element of an element marked s:nodeType="array."
In RDF, it’s the collection of child elements of an element marked rdf:parseType="Collection" or an rdf:Seq element.
All three support the notion of atomic values.
In XAML, it’s the textOnly content of an element or an attribute value.
In SOAP/1.2, it’s the child text of an element marked s:nodeType="simple."
In RDF, it’s the textOnly content of an element or an attribute value. Alternatively, it’s the child element of an element marked rdf:parseType="Literal"
To grok how these are similar and how they are different, consider the following data structure:
public class Person {
public string Name;
public double Age;
public Address[] Addresses;
}
public class Address {
public string Street;
public string CityStateZip;
}
In SOAP/1.2, an instance of Person would be written out like this:
<!-- example 1: SOAP/1.2 -->
<Person s:nodeType="struct">
<Name>Don Box</Name>
<Age xsi:type="xs:double">29</Age>
<Addresses s:nodeType="array" s:itemType="Address">
<what s:nodeType="struct">
<Street>One Microsoft Way</Street>
<CityStateZip>Redmond WA 98052</CityStateZip>
</what>
<ever s:nodeType="struct">
<Street>123 Main Street</Street>
<CityStateZip>Seattle WA 98091</CityStateZip>
</ever>
</Addresses>
</Person>
Note that for sequences (arrays in SOAP-speak), the name of the child elements is not significant. Also note that for simple values, I omitted the s:nodeType="simple" attribute, as it's unambiguous in all of these cases. I also omitted the xsi:type attribute where its value would be xs:string.
In XAML, the same data would look like this:
<!-- example 2a: XAML enf -->
<Person>
<Person.Name>Don Box</Person.Name>
<Person.Age>29</Person.Age>
<Person.Addresses>
<Address>
<Address.Street>One Microsoft Way</Address.Street>
<Address.CityStateZip>Redmond WA 98052</Address.CityStateZip>
</Address>
<Address>
<Address.Street>123 Main Street</Address.Street>
<Address.CityStateZip>Seattle WA 98091</Address.CityStateZip>
</Address>
</Person.Addresses>
</Person>
or equivalently, like this:
<!-- example 2b: XAML anf -->
<Person Name="Don Box" Age="29">
<Addresses>
<Address Street="One Microsoft Way" CityStateZip="Redmond WA 98052"/>
<Address Street="123 Main Street" CityStateZip="Seattle WA 98091" />
</Addresses>
</Person>
Note that the distinction between "record/struct" and "sequence/array" as well as the specific primitive type is implicit and can only be inferred with out-of-band metadata (probably a CLR assembly but one could get much more elaborate).
Here's the same data worked up in RDF several times:
<!-- example 3a: RDF anf-->
<Person Name="Don Box" Age="29" rdf:about="id1">
<Addresses rdf:parseType="Collection">
<Address rdf:parseType="Resource" Street="One Microsoft Way" CityStateZip="Redmond WA 98052"/>
<Address rdf:parseType="Resource" Street="123 Main Street" CityStateZip="Seattle WA 98091" />
</Addresses>
</Person>
<!-- example 3b: RDF enf-->
<Person rdf:about="#id1">
<Name>Don Box</Name>
<Age rdf:datatype="http://www.w3.org/2001/XMLSchema#double">29</Age>
<Addresses rdf:parseType="Collection">
<Address rdf:parseType="Resource">
<Street>One Microsoft Way</Street>
<CityStateZip>Redmond WA 98052</CityStateZip>
</Address>
<Address rdf:parseType="Resource">
<Street>123 Main Street</Street>
<CityStateZip>Seattle WA 98091</CityStateZip>
</Address>
</Addresses>
</Person>
<!-- example 3c: RDF sans typed node -->
<rdf:Description rdf:about="#id1">
<rdf:type rdf:resource="uri-prefix/Person</rdf:type>
<Name>Don Box</Name>
<Age rdf:datatype="http://www.w3.org/2001/XMLSchema#double">29</Age>
<Addresses rdf:parseType="Collection">
<rdf:Description rdf:parseType="Resource">
<rdf:type rdf:resource="uri-prefix/Address</rdf:type>
<Street>One Microsoft Way</Street>
<CityStateZip>Redmond WA 98052</CityStateZip>
</rdf:Description>
<rdf:Description rdf:parseType="Resource">
<rdf:type rdf:resource="uri-prefix/Address</rdf:type>
<Street>123 Main Street</Street>
<CityStateZip>Seattle WA 98091</CityStateZip>
</rdf:Description>
</Addresses>
</Person>
<!-- example 3d: RDF reified -->
<rdf:RDF>
<rdf:Description rdf:about="#s1">
<rdf:type rdf:resource="rdf-uri#Statement" />
<rdf:subject rdf:resource="#id1" />
<rdf:predicate rdf:resource="rdf-uri#type" />
<rdf:object rdf:resource="uri-prefix/Person" />
</rdf:Description>
<rdf:Description rdf:about="#s2">
<rdf:type rdf:resource="rdf-uri#Statement" />
<rdf:subject rdf:resource="#id1" />
<rdf:predicate rdf:resource="uri-prefix/Name" />
<rdf:object>Don Box</rdf:object>
</rdf:Description>
<rdf:Description rdf:about="#s3">
<rdf:type rdf:resource="rdf-uri#Statement" />
<rdf:subject rdf:resource="#id1" />
<rdf:predicate rdf:resource="uri-prefix/Age" />
<rdf:object rdf:datatype="http://www.w3.org/2001/XMLSchema#double">29</rdf:object>
</rdf:Description>
// remaining XML elided to reduce bandwidth costs
So, what are the tradeoffs?
SOAP/1.2 and RDF both support inline annotations to give hints about record vs. sequence and primitive type assignment. This makes life easier for processors that have to deal with data without having access to metadata (like SOAP intermediaries or generic data manipulation/query/transformation plumbing).
Between SOAP/1.2 and RDF, SOAP is considerably simpler as (a) there's only one way to represent a concept, not N ways and (b) that representation is just element-normal-form + a small number of annotations (s:nodeType, s:itemType). The use of ENF does make SOAP/1.2 encoding slightly less authorable than RDF's attribute-normal-form, but for someone writing importers, having to cope with N formats obviously make parsers bigger and more complicated.
Also, RDF expects you to buy into a URI-based resource model and only added the notion of "blank nodes" recently. For data transfer applications (which are exceedingly common), there often isn't a persistent URI or ID for the data being represented. Query results that use projection are a great example where having to cons up URI is overkill.
On the upside, RDF is unique amongst the three in its ability to have properties on properties on properties on…. Being able to treat an RDF statement as a resource that itself can have properties (in addition to the object or "value" of the property) is certainly powerful and appeals to the inner modeling geek.
As for XAML, it's reliance on out-of-band metadata to do the type assignment makes it somewhat different. If the XAML folks decide that that scenario is important (to date ChrisAn has been pretty explicit that it's a non-goal), my guess is it would look similar to SOAP/1.2 encoding with support for ANF where desired (and unambiguous).
At this point, I should point to Tim Bray's rants on the subject.
Posted
Feb 02 2005, 03:29 PM
by
don-box