Another attempt at refining the ontology that I've been working on for modelling vCard data in RDF. This time with a GRDDL transformation to extract RDF from hCard markup.

I've done another revision of the vCard ontology that I've been working on. I think the only really significant change is that I've reverted back to using property names (not labels) to identify classes of phone numbers, email addresses, and postal addresses. I had a chance to chat with Dan and Tim after the TAG face-to-face meeting and they convinced me that these are really relationships. It's not sufficient to label a phone number “work” because, for example, different people might have different relationships with the same phone number. (To be fair, some other folks made that point in comments on earlier essays.)

As a starting point, I created “work”, “mobile”, “fax”, and “home” phone number properties; “work”, “personal”, and “mobile” email addresses, and "work” and “home” postal addresses. I also created an explicitly “unlabeled” version of each property. If more subproperties are needed, they can always be added (and in OWL terms, any property that declares itself appropriately becomes a subproperty).

I removed the references to the http://www.w3.org/2003/01/geo/wgs84_pos# vocabulary, choosing instead to model the geographic information in the vCard vocabulary. This way, the vCard ontology stands alone and provides a mapping to and from vCards. I think it makes sense to augment your model with appropriate rules to construct, for example, FOAF and Geo properties from your vCards so that you can take advantage of other vocabularies in your data, but the vCard ontology doesn't have to do that for you.

Here's the ontology du jour:

  1# -*- N3 -*-
  2
  3@prefix owl: <http://www.w3.org/2002/07/owl#> .
  4@prefix xs: <http://www.w3.org/2001/XMLSchema#> .
  5@prefix v: <http://nwalsh.com/rdf/vCard#> .
  6@prefix geo: <http://www.w3.org/2003/01/geo/wgs84_pos#> .
  7@prefix foaf: <http://xmlns.com/foaf/0.1/> .
  8@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
  9@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
 10@prefix dc: <http://purl.org/dc/elements/1.1/> .
 11@prefix cc: <http://web.resource.org/cc/> .
 12
 13<http://nwalsh.com/rdf/vCard> a owl:Ontology;
 14    rdfs:comment "Norm's attempt at a vCard/hCard RDF ontology.";
 15    dc:title "An OWL Ontology for vCards";
 16    dc:description """This ontology attempts to model a subset of vCards
 17in RDF using modern (circa 2005) RDF best practices. The subset selected
 18is the same subset that the microformats community has adopted for use in
 19hCard.""";
 20    dc:creator <http://norman.walsh.name/knows/who#norman-walsh>;
 21    dc:date "2005-12-12";
 22    dc:rights "Copyright © 2005 Norman Walsh. This work is licensed under the Creative Commons Attribution-NonCommercial License.";
 23    cc:license <http://creativecommons.org/licenses/by-nc/2.0/> .
 24
 25# ------------------------------------------------------------
 26
 27v:VCard a owl:Class;
 28    rdfs:subClassOf
 29        [    a owl:Restriction;
 30             owl:maxCardinality "1"^^xs:nonNegativeInteger;
 31             owl:onProperty v:rev ];
 32    rdfs:label "vCard Class";
 33    rdfs:comment "Resources that are vCards." .
 34
 35v:Name a owl:Class;
 36    rdfs:subClassOf
 37        [    a owl:Restriction;
 38             owl:maxCardinality "1"^^xs:nonNegativeInteger;
 39             owl:onProperty v:family-name ];
 40    rdfs:subClassOf
 41        [    a owl:Restriction;
 42             owl:maxCardinality "1"^^xs:nonNegativeInteger;
 43             owl:onProperty v:given-name ];
 44    rdfs:subClassOf
 45        [    a owl:Restriction;
 46             owl:maxCardinality "1"^^xs:nonNegativeInteger;
 47             owl:onProperty v:additional-name ];
 48    rdfs:subClassOf
 49        [    a owl:Restriction;
 50             owl:maxCardinality "1"^^xs:nonNegativeInteger;
 51             owl:onProperty v:honorific-prefix ];
 52    rdfs:subClassOf
 53        [    a owl:Restriction;
 54             owl:maxCardinality "1"^^xs:nonNegativeInteger;
 55             owl:onProperty v:honorific-suffix ];
 56    rdfs:label "vCard Name Class";
 57    rdfs:comment "Resources that are vCard personal names." .
 58
 59v:Address a owl:Class;
 60    rdfs:subClassOf
 61        [    a owl:Restriction;
 62             owl:maxCardinality "1"^^xs:nonNegativeInteger;
 63             owl:onProperty v:post-office-box ];
 64    rdfs:subClassOf
 65        [    a owl:Restriction;
 66             owl:maxCardinality "1"^^xs:nonNegativeInteger;
 67             owl:onProperty v:extended-address ];
 68    rdfs:subClassOf
 69        [    a owl:Restriction;
 70             owl:maxCardinality "1"^^xs:nonNegativeInteger;
 71             owl:onProperty v:street-address ];
 72    rdfs:subClassOf
 73        [    a owl:Restriction;
 74             owl:maxCardinality "1"^^xs:nonNegativeInteger;
 75             owl:onProperty v:locality ];
 76    rdfs:subClassOf
 77        [    a owl:Restriction;
 78             owl:maxCardinality "1"^^xs:nonNegativeInteger;
 79             owl:onProperty v:region ];
 80    rdfs:subClassOf
 81        [    a owl:Restriction;
 82             owl:maxCardinality "1"^^xs:nonNegativeInteger;
 83             owl:onProperty v:postal-code ];
 84    rdfs:subClassOf
 85        [    a owl:Restriction;
 86             owl:maxCardinality "1"^^xs:nonNegativeInteger;
 87             owl:onProperty v:country-name ];
 88    rdfs:label "vCard Address Class";
 89    rdfs:comment "Resources that are vCard (postal) addresses." .
 90
 91v:Organization a owl:Class;
 92    rdfs:subClassOf
 93        [    a owl:Restriction;
 94             owl:maxCardinality "1"^^xs:nonNegativeInteger;
 95             owl:onProperty v:latitude ];
 96    rdfs:subClassOf
 97        [    a owl:Restriction;
 98             owl:maxCardinality "1"^^xs:nonNegativeInteger;
 99             owl:onProperty v:longitude ];
100    rdfs:label "vCard Organization Class";
101    rdfs:comment "Resources that are vCard organizations." .
102
103v:Geo a owl:Class;
104    rdfs:subClassOf
105        [    a owl:Restriction;
106             owl:maxCardinality "1"^^xs:nonNegativeInteger;
107             owl:onProperty v:organization-name ];
108    rdfs:subClassOf
109        [    a owl:Restriction;
110             owl:maxCardinality "1"^^xs:nonNegativeInteger;
111             owl:onProperty v:organization-unit ];
112    rdfs:label "vCard Geographic Location Class";
113    rdfs:comment "Resources that are vCard geographic locations." .
114
115# ------------------------------------------------------------
116
117v:fn a owl:DatatypeProperty;
118    rdfs:domain v:VCard;
119    rdfs:label "formatted name";
120    rdfs:comment "A formatted name of a person." .
121
122v:n a owl:ObjectProperty;
123    rdfs:domain v:VCard;
124    rdfs:range v:Name;
125    rdfs:label "name";
126    rdfs:comment "The components of the name of a person." .
127
128v:family-name a owl:DatatypeProperty;
129    rdfs:domain v:Name;
130    rdfs:label "family name";
131    rdfs:comment "A family name part of a person's name." .
132
133v:given-name a owl:DatatypeProperty;
134    rdfs:domain v:Name;
135    rdfs:label "given name";
136    rdfs:comment "A given name part of a person's name." .
137
138v:additional-name a owl:DatatypeProperty;
139    rdfs:domain v:Name;
140    rdfs:label "additional name";
141    rdfs:comment "An additional part of a person's name." .
142
143v:honorific-prefix a owl:DatatypeProperty;
144    rdfs:domain v:Name;
145    rdfs:label "honorific prefix";
146    rdfs:comment "An honorific prefix part of a person's name." .
147
148v:honorific-suffix a owl:DatatypeProperty;
149    rdfs:domain v:Name;
150    rdfs:label "honorific suffix";
151    rdfs:comment "An honorific suffix part of a person's name." .
152
153v:photo a owl:ObjectProperty;
154    rdfs:domain v:VCard;
155    rdfs:label "photo";
156    rdfs:comment "A photograph of a person." .
157
158v:bday a owl:DatatypeProperty;
159    rdfs:domain v:VCard;
160    rdfs:range xs:date;
161    rdfs:label "birthday";
162    rdfs:comment "The birthday of a person." .
163
164v:adr a owl:ObjectProperty;
165    rdfs:range v:Address;
166    rdfs:label "address";
167    rdfs:comment "A postal or street address of a person." .
168
169# A defined set of xxxAdr subproperties; more could be added
170
171v:workAdr a owl:ObjectProperty;
172    rdfs:subPropertyOf v:adr;
173    rdfs:range v:Address;
174    rdfs:label "Work address";
175    rdfs:comment "A work address of a person." .
176
177v:homeAdr a owl:ObjectProperty;
178    rdfs:subPropertyOf v:adr;
179    rdfs:range v:Address;
180    rdfs:label "Home address";
181    rdfs:comment "A home address of a person." .
182
183v:unlabeledAdr a owl:ObjectProperty;
184    rdfs:subPropertyOf v:adr;
185    rdfs:range v:Address;
186    rdfs:label "address";
187    rdfs:comment "An (explicitly) unlabeled address of a person." .
188
189v:post-office-box a owl:DatatypeProperty;
190    rdfs:domain v:Address;
191    rdfs:label "P.O. Box";
192    rdfs:comment "The post office box of a postal address." .
193
194v:extended-address a owl:DatatypeProperty;
195    rdfs:domain v:Address;
196    rdfs:label "extended";
197    rdfs:comment "The extended address of a postal address." .
198
199v:street-address a owl:DatatypeProperty;
200    rdfs:domain v:Address;
201    rdfs:label "street";
202    rdfs:comment "The street address of a postal address." .
203
204v:locality a owl:DatatypeProperty;
205    rdfs:domain v:Address;
206    rdfs:label "locality";
207    rdfs:comment "The locality (e.g., city) of a postal address." .
208
209v:region a owl:DatatypeProperty;
210    rdfs:domain v:Address;
211    rdfs:label "region";
212    rdfs:comment "The region (e.g., state or province) of a postal address." .
213
214v:postal-code a owl:DatatypeProperty;
215    rdfs:domain v:Address;
216    rdfs:label "postal code";
217    rdfs:comment "The postal code (e.g., U.S. ZIP code) of a postal address." .
218
219v:country-name a owl:DatatypeProperty;
220    rdfs:domain v:Address;
221    rdfs:label "country";
222    rdfs:comment "The country of a postal address." .
223
224v:label a owl:DatatypeProperty;
225    rdfs:domain v:Address;
226    rdfs:label "address label";
227    rdfs:comment "The formatted version of a postal address (a string with embedded line breaks, punctuation, etc.)." .
228
229v:tel a owl:ObjectProperty;
230    rdfs:domain v:VCard;
231    rdfs:label "phone";
232    rdfs:comment "A telephone number of a person." .
233
234# A defined set of xxxTel subproperties; more could be added
235
236v:workTel a owl:ObjectProperty;
237    rdfs:subPropertyOf v:tel;
238    rdfs:label "Work phone";
239    rdfs:comment "A work phone number of a person." .
240
241v:mobileTel a owl:ObjectProperty;
242    rdfs:subPropertyOf v:tel;
243    rdfs:label "Mobile phone";
244    rdfs:comment "A mobile phone number of a person." .
245
246v:fax a owl:ObjectProperty;
247    rdfs:subPropertyOf v:tel;
248    rdfs:label "Fax";
249    rdfs:comment "A fax number of a person." .
250
251v:homeTel a owl:ObjectProperty;
252    rdfs:subPropertyOf v:tel;
253    rdfs:label "Home phone";
254    rdfs:comment "A home phone number of a person." .
255
256v:unlabeledTel a owl:ObjectProperty;
257    rdfs:subPropertyOf v:tel;
258    rdfs:label "phone";
259    rdfs:comment "An (explicitly) unlabeled phone number of a person." .
260
261v:email a owl:ObjectProperty;
262    rdfs:domain v:VCard;
263    rdfs:label "email";
264    rdfs:comment "An email address." .
265
266# A defined set of xxxEmail subproperties; more could be added
267
268v:workEmail a owl:ObjectProperty;
269    rdfs:subPropertyOf v:email;
270    rdfs:label "Work email";
271    rdfs:comment "A work email address of a person." .
272
273v:personalEmail a owl:ObjectProperty;
274    rdfs:subPropertyOf v:email;
275    rdfs:label "Personal email";
276    rdfs:comment "An email address unaffiliated with any particular organization or employer; a personal email address." .
277
278v:mobileEmail a owl:ObjectProperty;
279    rdfs:subPropertyOf v:email;
280    rdfs:label "Mobile email";
281    rdfs:comment "A mobile email address of a person." .
282
283v:unlabeledEmail a owl:ObjectProperty;
284    rdfs:subPropertyOf v:email;
285    rdfs:label "email";
286    rdfs:comment "An (explicitly) unlabeled email address of a person." .
287
288v:mailer a owl:DatatypeProperty;
289    rdfs:domain v:VCard;
290    rdfs:label "mailer";
291    rdfs:comment "A mailer associated with a vCard." .
292
293v:tz a owl:DatatypeProperty;
294    rdfs:domain v:VCard;
295    rdfs:label "timezone";
296    rdfs:comment "A timezone associated with a person." .
297
298v:geo a owl:ObjectProperty;
299    rdfs:domain v:VCard;
300    rdfs:range v:Geo;
301    rdfs:label "geographic location";
302    rdfs:comment "A geographic location associated with a person." .
303
304v:latitude a owl:DatatypeProperty;
305    rdfs:domain v:Geo;
306    rdfs:range xs:double;
307    rdfs:label "latitude";
308    rdfs:comment "The latitude of a geographic location." .
309
310v:longitude a owl:DatatypeProperty;
311    rdfs:domain v:Geo;
312    rdfs:range xs:double;
313    rdfs:label "longitude";
314    rdfs:comment "The longitude of a geographic location" .
315
316v:title a owl:DatatypeProperty;
317    rdfs:domain v:VCard;
318    rdfs:label "title";
319    rdfs:comment "A person's title." .
320
321v:role a owl:DatatypeProperty;
322    rdfs:domain v:VCard;
323    rdfs:label "role";
324    rdfs:comment "A role a person plays within an organization." .
325
326v:logo a owl:ObjectProperty;
327    rdfs:domain v:VCard;
328    rdfs:label "logo";
329    rdfs:comment "A logo associated with a person or their organization." .
330
331v:agent a owl:ObjectProperty;
332    rdfs:domain v:VCard;
333    rdfs:range v:VCard;
334    rdfs:label "agent";
335    rdfs:comment "A person that acts as one's agent." .
336
337v:org a owl:ObjectProperty;
338    rdfs:domain v:VCard;
339    rdfs:range v:Organization;
340    rdfs:label "organization";
341    rdfs:comment "An organization associated with a person." .
342
343v:organization-name a owl:DatatypeProperty;
344    rdfs:domain v:Organization;
345    rdfs:label "name";
346    rdfs:comment "The name of an organization." .
347
348v:organization-unit a owl:DatatypeProperty;
349    rdfs:domain v:Organization;
350    rdfs:label "unit";
351    rdfs:comment "The name of a unit within an organization." .
352
353v:category a owl:DatatypeProperty;
354    rdfs:domain v:VCard;
355    rdfs:label "category";
356    rdfs:comment "A category of a vCard." .
357
358v:note a owl:DatatypeProperty;
359    rdfs:domain v:VCard;
360    rdfs:label "notes";
361    rdfs:comment "Notes about a person on a vCard." .
362
363v:rev a owl:DatatypeProperty;
364    rdfs:domain v:VCard;
365    rdfs:range [ owl:unionOf (xs:date xs:dateTime) ];
366    rdfs:label "revision";
367    rdfs:comment "The timestamp of a revision of a vCard." .
368
369v:sort-string a owl:DatatypeProperty;
370    rdfs:domain v:VCard;
371    rdfs:label "sort";
372    rdfs:comment "A version of a person's name suitable for collation." .
373
374v:sound a owl:ObjectProperty;
375    rdfs:domain v:VCard;
376    rdfs:label "sound";
377    rdfs:comment "A sound (e.g., a greeting or pronounciation) of a person." .
378
379v:uid a owl:DatatypeProperty, owl:InverseFunctionalProperty;
380    rdfs:domain v:VCard;
381    rdfs:label "uid";
382    rdfs:comment "A UID of a person's vCard." .
383
384v:url a owl:ObjectProperty;
385    rdfs:domain v:VCard;
386    rdfs:label "url";
387    rdfs:comment "A URL associated with a person." .
388
389v:class a owl:DatatypeProperty;
390    rdfs:domain v:VCard;
391    rdfs:label "class";
392    rdfs:comment "A class (e.g., public, private, etc.) of a vCard." .
393
394v:key a owl:ObjectProperty;
395    rdfs:domain v:VCard;
396    rdfs:label "key";
397    rdfs:comment "A key (e.g, PKI key) of a person." .

Finally, I created a transformation suitable for use with GRDDL that extracts vCard RDF from HTML marked up with hCard. It really isn't tested as fully as it should be, but it appeared to do the right thing with the test data I fed to it.

I think I've taken this about as far as I should all by myself. If there's value in this ontology, I think it makes sense to find some way of moving maintenance to a community process. Dan suggested an incubator group, which seems reasonable. That would probably get it a namespace URI in the W3C domain which would certainly lend it an air of respectability. Anyway, I think it's in pretty good shape and I'm willing to work on maintaining it in public if we can find some reasonably low cost way of doing so.

Until then, it'll live on in its current namespace, but I probably won't write about it again unless something dramatic changes.

Comments:

Heyup Norm, there are rdfs:subClassOfs + property restrictions associated with properties instead of classes.

Posted by Benjamin Nowack on 13 Dec 2005 @ 08:57am UTC #

Really useful, thanks.

Posted by Susan on 13 Dec 2005 @ 10:35am UTC #

*Blush* Thank you, Benjamin. I knew I was rushing. Fixed.

Posted by Norman Walsh on 13 Dec 2005 @ 11:20am UTC #

"1"^^xs:nonNegativeInteger is a mouthful; in N3/turtle, you can just write 1.

Posted by Dan Connolly on 13 Dec 2005 @ 10:23pm UTC #

Hi Norm, not sure anout the cardinality on v:rev. What happens whan a vcard changes (say you add a v:sound), does it gets a new uid and it becomes a new and distinct vcard-resource, or does it keep the uid and get's the v:rev replaced which makes the new graph a contradiction to the previous one? I think the easiest would be to allow multiple v:rev.

Posted by Reto on 14 Dec 2005 @ 12:59pm UTC #

Yeah, I don't know about the cardinality of v:rev either. My thinking was: if tracking revisions is important enough to you that you've bothered to put a v:rev property on your objects, you probably want to keep them distinct somehow. Once you find a v:VCard with two v:rev properties, you can be sure you've got no way to reconstruct either revision from the collection of properties on that v:VCard.

But it's probably still a bad idea to impose the cardinality restriction, I suppose.

Posted by Norman Walsh on 14 Dec 2005 @ 01:13pm UTC #

Could you provide a couple of instances please Norm, one form or another; fictitious or otherwise - it would help in comprehension for those trailing?

TIA DaveP

Posted by Dave Pawson on 16 Dec 2005 @ 10:29am UTC #
Comments on this essay are closed. Thank you, spammers.