XFN → FOAF
by Toby Inkster
(Contributors: Gabriele Renzi)
A number of people have expressed an interest in extracting RDF-like data from XFN and hCard. The problem is that while XFN is interpreted as representing a relationship between two people, it actually encodes a relationship between two URIs.
This page describes a technique for figuring out which people these URIs represent. It is not an attempt to describe a new specification or standard, but rather, a set of best practices. Two algorithms are described: a "high-bandwidth" version which requires web crawling, and a "low-bandwidth" version which uses only the information found on the initial page.
For most of the examples on this page, the following XFN link will be used:
<a rel="friend met" href="http://bob.example.net">Bob Smith</a>
which has been found on Alice Jones' web page at http://alice.example.net.
Determining the subject
- Find the representative hCard for the current page.
- In the high-bandwidth situation, parsers MAY crawl
rel=melinks in order to find a "better" representative hCard, where the meaning of "better" is to be defined by the parser itself.
- (Parsers SHOULD impose a depth limit for crawling.)
- If no hCard for the subject has been found, the subject is a person represented by the following RDF triples:
_:bnode01 a foaf:Person; foaf:page <http://alice.example.net>.
A parser which understands RDFa or other semantics may use additional techniques to determine the subject of the link, but those are beyond the scope of the Microformats wiki.
Determining the predicate
This is the easiest step.
rel="me" the predicate is
For other relationships, the local name of the predicate is the same as the rel value, and the namespace URI is defined as
http://vocab.sindice.com/xfn#. Many non-me XFN values can be considered to be refinements of foaf:knows. As an example, the fully qualified URIs for the predicates associated with
rel="met friend" are:
Which XFN values are refinements of
foaf:knows? This is a matter of debate. Although normally, say, a parent and child will have a close relationship, there are instances where they may have never met, and not know each other at all. The current list of XFN values which I treat as refinements of
foaf:knows are: acquaintance, friend, met, co-resident, spouse, crush, date and sweetheart. Depending upon your application, you may wish to narrow or broaden your scope.
Determining the object
- If the link element is a descendant of an element with
class="vcard"which is not the representative hCard for the page, then this hCard is taken to represent the person who is the object of the relationship.
- In the high-bandwidth situation, parsers MAY follow the link target to look for a representative hCard for the object.
- (Parsers MAY then further follow rel="me" links from http://bob.example.net in order to find a "better" representative hCard for Bob.)
- If no hCard representing the object of the relationship has been found, then the object is taken to be a
foaf:namecorresponding to the link text and, depending on the kind of link provided in the
foaf:mbox(for "mailto:" links),
foaf:mbox_sha1sum(for "urn:sha1:" URLs),
foaf:img(image links, determined by
typeattribute or HTTP headers, not by file name) or
foaf:page(all other links). Our example would generate the following triples:
_:bnode02 a foaf:Person; foaf:page <http://bob.example.net>; foaf:name "Bob Smith".
A parser which understands RDFa or other semantics may use additional techniques to determine the object of the link, but those are beyond the scope of the Microformats wiki.
Is the link text really a person's name? sometimes it will be, sometimes it won't. If the link is part of an hCard which has used
<a class="fn url" ...>, then it should be safe to suppose that it is a name, but at other times this is more of a risk. Often the link text will be a nickname. This is OK, as the definition of the
foaf:name term is sufficiently broad to cover these cases. In other cases, it might not be a name at all. Depending on your application's needs, you may want to consider using
rdfs:label instead of
foaf:name or simply ignoring the link text completely.
The following example is assumed to have been found at http://alice.example.net. For simplicity's sake, we assume the low-bandwidth situation.
<html lang="en"> <title>Alice Jones</title> <div class="vcard"> <h1 class="fn">Alice Jones</h1> <p class="adr"> <span class="locality">Sydney</span>, <span class="country-name">Australia</span>. </p> <p> <a href="http://alice.example.com/blog/" rel="me" class="url"> Alice's Blog </a> </p> </div> <h2>Friends & Contacts</h2> <ul> <li class="vcard"> <a class="fn url" href="http://bob.example.net" rel="friend met"> Bob Smith </a> </li> <li> <a href="http://carol.example.net" rel="co-worker met"> Carol Brown </a> </li> <li> <a href="http://dave.example.net" rel="friend neighbor met"> Dave Wong </a> </li> <li> <a href="http://eve.example.net" rel="adversary met"> Eve Ville </a> </li> </ul> <address class="vcard"> Page maintained by <a href="http://eve.example.net" class="url uid" >Eve Ville</a>. Contact <a class="email" href="mailto:email@example.com" >firstname.lastname@example.org</a> for corrections. (I know I'm not the most trustworthy of sources.) </address> </html>
The subject of all the XFN links on the page is the hCard for Alice Jones at the top of the page. This is determined in step two of the representative hCard parsing procedure because it contains
The next XFN link is the one labelled "Bob Smith". Because the link is part of an hCard, the person described by the hCard is the object of the link.
For the next two XFN links, there exist no hCards that represent the objects. We can gather some information about them from the link element itself: their
foaf:page. (Note that FOAF defines foaf:name very loosely, so it's OK if the link text is a nickname.)
Although at first glance the XFN link for Eve Ville looks similar, there is in fact an hCard later on in the page with a UID matching the XFN link target, so using rule #2 for determining the object, we use this hCard as the object of the XFN relationship. Note that "adversary" is not an XFN rel value, so is beyond the scope of this document.
Possible RDF Output
The following is a possible RDF/XML representation of the information in the example above.
<?xml version="1.0"?> <rdf:RDF xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xfn="http://vocab.sindice.com/xfn#" xmlns="http://www.w3.org/2006/vcard/ns#"> <!-- Alice --> <VCard rdf:nodeID="bnode01"> <!-- data from Alice's hCard --> <fn>Alice Jones</fn> <adr rdf:parseType="Resource"> <locality>Sydney</locality> <country-name>Australia</country-name> </adr> <url rdf:resource="http://alice.example.com/blog/" /> <!-- data from Alice's XFN links --> <foaf:page rdf:resource="http://alice.example.com/blog/" /> <foaf:knows rdf:nodeID="bnode02" /> <xfn:met rdf:nodeID="bnode02" /> <xfn:friend rdf:nodeID="bnode02" /> <foaf:knows rdf:nodeID="bnode03" /> <xfn:met rdf:nodeID="bnode03" /> <xfn:co-worker rdf:nodeID="bnode03" /> <foaf:knows rdf:nodeID="bnode04" /> <xfn:met rdf:nodeID="bnode04" /> <xfn:friend rdf:nodeID="bnode04" /> <xfn:neighbor rdf:nodeID="bnode04" /> <foaf:knows rdf:nodeID="bnode05" /> <xfn:met rdf:nodeID="bnode05" /> </VCard> <!-- Bob, data from hCard --> <VCard rdf:nodeID="bnode02"> <fn>Bob Smith</fn> <url rdf:resource="http://bob.example.net" /> </VCard> <!-- Carol, implied data --> <foaf:Person rdf:nodeID="bnode03"> <foaf:name>Carol Brown</foaf:name> <foaf:page rdf:resource="http://carol.example.net" /> </foaf:Person> <!-- Dave, implied data --> <foaf:Person rdf:nodeID="bnode04"> <foaf:name>Dave Wong</foaf:name> <foaf:page rdf:resource="http://dave.example.net" /> </foaf:Person> <!-- Eve, data from hCard --> <VCard rdf:nodeID="bnode05"> <fn>Eve Ville</fn> <url rdf:resource="http://eve.example.net" /> <uid>http://eve.example.net"</uid> <email rdf:resource="mailto:email@example.com" /> </VCard> </rdf:RDF>
Note that some personal data for contacts is expressed in the FOAF vocabulary, and some information is expressed in vCard/hCard vocabulary. User agents may use OWL or another technique to draw equivalencies between vocabularies, such as taking
fn to be equivalent to
Organisation hCards and XFN
If either the subject or object hCard represents an organisation (rather than a person), the following relationships are meaningless:
Explicit reverse relationships may be provided by authors using the
rev attribute. These should be interpreted by parsers in exactly the same manner as described above, however subject and object must be swapped.
The XFN 1.1 profile explicitly mentions inverses for some XFN relationships, and lists which relationships are symmetric (i.e. their own inverse). For example, as Alice met Bob, it is implied that Bob met Alice. Parsers may use the information in the profile to make explicit the implicit reverse relationships.
- Re: XFN is getting smoked by FOAF — Toby Inkster, 2008-03-11
- Re: XFN + hCard — Toby Inkster, 2008-03-12
- Re: A (big) problem with XFN: identity of source and target not findable — Toby Inkster, 2008-03-18
- Re: A (big) problem with XFN: identity of source and target not findable — David Janes, 2008-03-18
- Re: A (big) problem with XFN: identity of source andtarget not findable — Toby Inkster, 2008-03-18
- Re: A (big) problem with XFN: identity of source andtarget not findable — Roger L Costello, 2008-03-20
- Re: Coding mbox_sha1sum in XFN — Toby Inkster, 2008-04-26
- Sindice: Expressing the XFN microformat in RDF (largely compatible with this page) — Richard Cyganiak, 2008-06-27
- XFN+hCard supporting friends lists - examples in the wild of XFN usage.
- XFN Implementations
- xfn-cheatsheet - see http://gmpg.org/xfn/join and existing rel values
- XFN FAQ
- xfn-clarifications - should eventually be rolled into XFN info on GMPG.org.
- XFN Brainstorming
Microformats to RDF
- XHTML Enemies Network - spoof of XFN. Can be easily implemented using the same algorithm.
- XHTML Professionals Network - proposed extension to XFN to deal with corporate relationships. When/if completed, will probably be implementable using the algorithm on this page.