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.
Really useful, thanks.
*Blush* Thank you, Benjamin. I knew I was rushing. Fixed.
"1"^^xs:nonNegativeInteger is a mouthful; in N3/turtle, you can just write 1.
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.
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.
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