Superseded by Extracting vCards from hCard markup, 12 Dec 2005.  Supersedes An address book ontology, 25 Nov 2005.  Supersedes An address book ontology (take 2), 30 Nov 2005.

Modelling vCards in RDF

Volume 8, Issue 157; 05 Dec 2005; last modified 08 Oct 2010

Yet another stab at modelling names and addresses.

For all the appeal of AddressVocab, it's just too hard to use. Starting from my “contacts” data, there's no way to get to the fine granularity of that vocabulary except by painful heuristics. And really, it's way too fine for my purposes.

Much better, as Roberto Garcia suggested, would be to simply use vCard.

The problem with vCard is that the existing RDF/XML vocabulary for vCards isn't very good. With all due respect to the author, RDF and RDF best practices have evolved since its inception in early 2001.

Dan Connolly suggested that (a) some work in the area of defining a more modern RDF ontology for vCard data would be valuable and (b) that starting with what the microformats folks have done with a profile of vCard for hCard would be a good place to start.

Well, heck, lead me far enough down the path and even I can figure out what direction to go.

So I started with the hCard profile and the vCard MIME Directory Profile and tried to construct an ontology in the most straightforward way I could.

The result has five classes and about forty properties. In broad strokes:

  • There's a VCard class (with the bulk of the properties: fn, n, adr, email, phone, etc.) for vCards themselves,

  • a Name class to hold the structured name properties (family-name, given-name, etc.),

  • an Address class to hold the structured (postal) address properties (post-office-box, street-address, locality, etc.),

  • an Organization class to hold the properties associated with an organization (organization-name and organization-unit),

  • and a Key class to hold the properties associated with a key (key-value and possibly encoding).

I borrrowed the Point class from the http://www.w3.org/2003/01/geo/wgs84_pos# vocabulary for the geo property. I was tempted to borrow the FOAF name properties but decided there wasn't quite enough correlation between the vCard parts and the FOAF parts.

Here's what I ended up with:

# -*- N3 -*-

@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix xs: <http://www.w3.org/2001/XMLSchema#> .
@prefix v: <http://nwalsh.com/rdf/vCard#> .
@prefix geo: <http://www.w3.org/2003/01/geo/wgs84_pos#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

<http://nwalsh.com/rdf/vCard> a owl:Ontology;
    rdfs:comment "Norm's attempt at a vCard/hCard RDF ontology." .

# ------------------------------------------------------------

v:VCard a owl:Class;
    rdfs:comment "Resources that are vCards." .

v:Name a owl:Class;
    rdfs:comment "Resources that are vCard personal names." .

v:Address a owl:Class;
    rdfs:comment "Resources that are vCard (postal) addresses." .

v:Organization a owl:Class;
    rdfs:comment "Resources that are vCard organizations." .

v:Key a owl:Class;
    rdfs:comment "Resources that are vCard keys." .

# ------------------------------------------------------------

v:type a owl:DatatypeProperty;
    rdfs:comment "Several vCard resources can have an associated type." .

v:encoding a owl:DatatypeProperty;
    rdfs:comment "Several vCard resources can have an associated encoding." .

# ------------------------------------------------------------

v:fn a owl:DatatypeProperty;
    rdfs:domain v:VCard;
    rdfs:comment "A formatted personal name." .

v:n a owl:ObjectProperty;
    rdfs:domain v:VCard;
    rdfs:range v:Name;
    rdfs:comment "A vCard personal name resource." .

v:family-name a owl:DatatypeProperty;
    rdfs:domain v:Name;
    rdfs:comment "A person's family name." .

v:given-name a owl:DatatypeProperty;
    rdfs:domain v:Name;
    rdfs:comment "A person's given name." .

v:additional-name a owl:DatatypeProperty;
    rdfs:domain v:Name;
    rdfs:comment "A person's additional name." .

v:honorific-prefix a owl:DatatypeProperty;
    rdfs:domain v:Name;
    rdfs:comment "A person's honorific prefix." .

v:honorific-suffix a owl:DatatypeProperty;
    rdfs:domain v:Name;
    rdfs:comment "A person's honorific suffix." .

v:photo a owl:ObjectProperty;
    rdfs:domain v:VCard;
    rdfs:comment "A photograph." .

v:bday a owl:DatatypeProperty;
    rdfs:domain v:VCard;
    rdfs:range xs:date;
    rdfs:comment "A birthday." .

v:adr a owl:ObjectProperty;
    rdfs:range v:Address;
    rdfs:comment "A vCard address resource" .

v:post-office-box a owl:DatatypeProperty;
    rdfs:domain v:Address;
    rdfs:comment "The post office box of a postal address." .

v:extended-address a owl:DatatypeProperty;
    rdfs:domain v:Address;
    rdfs:comment "The extended address of a postal address." .

v:street-address a owl:DatatypeProperty;
    rdfs:domain v:Address;
    rdfs:comment "The street address of a postal address." .

v:locality a owl:DatatypeProperty;
    rdfs:domain v:Address;
    rdfs:comment "The locality (e.g., city) of a postal address." .

v:region a owl:DatatypeProperty;
    rdfs:domain v:Address;
    rdfs:comment "The region (e.g., state or province) of a postal address." .

v:postal-code a owl:DatatypeProperty;
    rdfs:domain v:Address;
    rdfs:comment "The postal code (e.g., U.S. ZIP code) of a postal address." .

v:country-name a owl:DatatypeProperty;
    rdfs:domain v:Address;
    rdfs:comment "The country of a postal address." .

v:label a owl:DatatypeProperty;
    rdfs:domain v:Address;
    rdfs:comment "The formatted version of a postal address (a string with embedded line breaks, punctuation, etc.)." .

v:tel a owl:ObjectProperty;
    rdfs:domain v:VCard;
    rdfs:comment "A telephone number." .

v:email a owl:ObjectProperty;
    rdfs:domain v:VCard;
    rdfs:comment "An email address." .

v:mailer a owl:DatatypeProperty;
    rdfs:domain v:VCard;
    rdfs:comment "The mailer associated with a vCard." .

v:tz a owl:DatatypeProperty;
    rdfs:domain v:VCard;
    rdfs:comment "The timezone." .

v:geo a owl:ObjectProperty;
    rdfs:domain v:VCard;
    rdfs:range geo:Point;
    rdfs:comment "Geographic information (a latitude and longitude)" .

v:title a owl:DatatypeProperty;
    rdfs:domain v:VCard;
    rdfs:comment "A person's title." .

v:role a owl:DatatypeProperty;
    rdfs:domain v:VCard;
    rdfs:comment "The role a person plays within an organization." .

v:logo a owl:ObjectProperty;
    rdfs:domain v:VCard;
    rdfs:comment "A logo." .

v:agent a owl:ObjectProperty;
    rdfs:domain v:VCard;
    rdfs:range v:VCard;
    rdfs:comment "A person that acts as one's agent." .

v:org a owl:ObjectProperty;
    rdfs:domain v:VCard;
    rdfs:range v:Organization;
    rdfs:comment "An organization." .

v:organization-name a owl:DatatypeProperty;
    rdfs:domain v:Organization;
    rdfs:comment "The name of an organization." .

v:organization-unit a owl:DatatypeProperty;
    rdfs:domain v:Organization;
    rdfs:comment "The name of a unit within a larger organization." .

v:category a owl:DatatypeProperty;
    rdfs:domain v:VCard;
    rdfs:comment "A category." .

v:note a owl:DatatypeProperty;
    rdfs:domain v:VCard;
    rdfs:comment "Notes." .

# How do I make the range a date or a dateTime?
v:rev a owl:DatatypeProperty;
    rdfs:domain v:VCard;
    rdfs:range xs:dateTime;
    rdfs:comment "The timestamp of a revision." .

v:sort-string a owl:DatatypeProperty;
    rdfs:domain v:VCard;
    rdfs:comment "A sort string." .

v:sound a owl:ObjectProperty;
    rdfs:domain v:VCard;
    rdfs:comment "A sound (e.g., a greeting)." .

v:uid a owl:DatatypeProperty;
    rdfs:domain v:VCard;
    rdfs:comment "A UID." .

v:url a owl:ObjectProperty;
    rdfs:domain v:VCard;
    rdfs:comment "A URL." .

v:class a owl:DatatypeProperty;
    rdfs:domain v:VCard;
    rdfs:comment "The class (e.g., public, private, etc.) of a vCard." .

v:key a owl:ObjectProperty;
    rdfs:domain v:VCard;
    rdfs:range v:Key;
    rdfs:comment "The key (e.g, PKI key)." .

v:key-value a owl:DatatypeProperty;
    rdfs:domain v:Key;
    rdfs:comment "The key value." .

There's certainly room for discussion about whether this is exactly the right selection of properties to include, and whether these are exactly the right modelling choices, but it “feels” pretty good. I'll try to incorporate what feedback I get.

In the larger context of my address book, I'm currently just using the adr property on my Contact class, instead of trying to model each of my contacts as a vCard. But that's something I can change later if there seems to be value in modelling them as vCards.

Comments

Is it just me, or is it odd to give an organisation an organisation-name property. Either I'd give a person an organisation-name property, or the organisation a name property.

—Posted by Sjoerd Visscher on 06 Dec 2005 @ 10:12 UTC #

Norm, how about changing v:n to v:name, and v:fn to v:formattedName or, better yet, v:displayName? In any case, the one or two letter initials ought to go.

—Posted by Bruce D'Arcus on 06 Dec 2005 @ 01:37 UTC #

Another name issue: while almost all of the name properties (given, family, etc.) can denote order coupled with language, the additional-name property cannot. Not sure if there's any easy way around this though.

—Posted by Bruce D'Arcus on 06 Dec 2005 @ 01:59 UTC #

There are a few useful features of vCard that aren't supported. Eg home and business phone numbers, and prefered numbers:

TEL;TYPE=home,cell:555-1111
TEL;TYPE=work,cell:555-2222
TEL;TYPE=work,pref:555-3333

These attributes also apply to addresses and emails and are used quite frequently in practice.

Many vCard fields allow lists of multiple values using comma (whilst still allowing comma to be used as a comma by backslash escaping it). Eg, someone with two middle names:

N:Stevenson;John;Philip,Paul;;

or an address with two street address lines:

ADR:;;10\, Downing Street,Westminster;London;;;UK

Features such as the "group." notation for grouping properties would be difficult to implement in RDF, and are so rarely used that it doesn't seem worth trying to support them. vCard is fairly complicated, so it will be difficult to come up with something that makes simple cases simple, but can also be extended to cope with more complicated cases. It would still be useful to come up with a "vCard/RDF Basic" vocabularly though, whilst still allow someone to add "vCard/RDF Super" properties if they needed to do anything fancier (such as ones which take rdf:Collections as values.)

How do you intend to implement things like Photo, btw? As base-64 encoded literals?

—Posted by David Powell on 06 Dec 2005 @ 02:50 UTC #
I've modelled phone numbers as URIs, so type is simply a property of that URI:
<rdf:Description rdf:about="tel:555-1111"
                 xmlns:v="http://nwalsh.com/rdf/vCard#">
  <v:type>home</v:type>
  <v:type>cell</v:type>
</rdf:Description>
Similarly, middle name is a property and that property can occur more than once with different values. For better or worse, this doesn't preserve order within the group. I modelled photo as an object so you can either point to it with a URI or use a data: URI to encode it "in place".
—Posted by Norman Walsh on 06 Dec 2005 @ 03:33 UTC #

I’m not sure if any of the existing ontologies follow the approach I want to outline, since I have no appreciable experience with RDF, as of yet (one day I intend to fix that), but I’ve done my share of RDBMS schema modelling, so I do have some opinions.

I believe assigning labels to phone numbers is not the right way to model this. The cleaner approach is to capture that a person has multiple roles; a role is what each piece of contact information is associated with. So rather than modelling a phone number as label + number attached to a person, it is just a number attached to a role attached to a person. Likewise, an email address is attached to a role.

That way you can capture any amount of detail in a very finegrained fashion that simply applying labels to types of contact info cannot match. All sorts of cool things are possible, like easily marking the entire “Acme Corp” role of “John Doe” as “past” when he leaves the company (and discard it as one or keep it for historical reference; how about attaching from/until data to roles?).

You could also allow roles to aggregate other roles, say, when “John Doe” works at two different “Acme Corp” offices; when you don’t know which one he is at today, you want to know all phone numbers he might be reached at.

Going on, you can then model organisations as a special case of a role which has no owner and may aggregate roles belonging to any person.

(If you try to model a person as a special case of a role things get hairy. I tried to sketch this out a number of ways and decided I was running out of oxygen.)

—Posted by Aristotle Pagaltzis on 06 Dec 2005 @ 04:04 UTC #

Given a completely free hand, I think I would take an approach along the lines you suggest, Aristotle. However, that's not a model that any PIM I've used actually takes. Nor is it consistent with my understanding of the vCard model. I'm not inclined to use a model that won't map conveniently onto my actual address book.

—Posted by Norman Walsh on 06 Dec 2005 @ 04:57 UTC #

Sorry, I forgot about the tel: URLs. There could potentially be problems doing that though; PREF is really a property of the relationship rather than the number itself. I share a desk phone at work, I might choose to mark that phone as preferred, but if I did then anyone else using that phone would get it marked as preferred too.


Aristotle: the "group." notation in vCard lets you do some of the things that you mention, but I've never seen it used in practice. An example would be:

x.TEL;TYPE=WORK:555-2222
x.EMAIL:old@example.com
x.NOTE:Old workplace
y.TEL;TYPE=WORK:555-1111
y.EMAIL:new@example.com
y.NOTE:New workplace

Theoretically applications should be able to consume property groups, even if they ignore the grouping.

—Posted by David Powell on 06 Dec 2005 @ 05:43 UTC #

hm, I don't see the disctinction between adress objects that serve as postal or work or mail or other address.

x - hasPostalAddress - y.

y a Address.

x - hasWorkAddress - z

z a Address.

?

—Posted by leobard on 07 Dec 2005 @ 05:24 UTC #

Norm, you say "I've modelled phone numbers as URIs, so type is simply a property of that URI". I feel that is broken: the home/work/vacation is a relationship between me and a contact point which can have adresss and phone number. The same location may be Jack and Jill's home and Jack's workplace, but not Jill's workplace. The contact ontology, which was my take on this many years ago, uses a class of Location for this.

Can't you concluse from the fact that your PIM says someone has a work phone number that they have a workplace which has a phone number? Aristotle, you call these roles, and I think thats the right structure, though I don't think 'role' is the rigth word.

The Mac Addressbook allows you to create your one values for home/work/mobile/etc whcih suggests an advantage in modelling that relationship explicity and dseparately from the phone number.

This really depends on whether the goal is to make a nice ontology or to make the closest one to vcard. I'd be happy with two ontologies with well-known conversions between them.

Oh, and by the way, would you like a w3.org namespace for that in case it catches on and develops (with he greatest of restect for the existing one!).

—Posted by Tim Berners-Lee on 21 Aug 2006 @ 02:27 UTC #
Hi Tim. I've already changed the way I model phone numbers (probably based on a comment you made in some other forum :-). I now do it this way:
c:phone a owl:ObjectProperty;
    rdfs:label "Phone" .

c:homePhone a owl:ObjectProperty;
    rdfs:subPropertyOf c:phone;
    rdfs:label "Home" .

c:workPhone a owl:ObjectProperty;
    rdfs:subPropertyOf c:phone;
    rdfs:label "Work" .
...
Then I can say: who:tbl c:workPhone "555-1212"; c:homePhone "555-1212" . etc.

Better?

With respect to the W3C namespace, I'm game, I suppose. Though I guess we should have some public discussion somewhere to see if there's actually consensus about the model. As you point out, there's a tradeoff to be made between "a nice ontology" and "modelling vCard". Given that there were several other attempts out there to do ontologies for this sort of thing, I decided to make mine different by attempting to model vCard as closely as possible. But I'm prepared to be persuaded that it would be more valuable if it diverged here and there. (e.g. Bruce's comment about the ugly, short names.)

—Posted by Norman Walsh on 23 Aug 2006 @ 05:39 UTC #