Supersedes An address book ontology, 25 Nov 2005.  Supersedes An address book ontology (take 2), 30 Nov 2005.  Supersedes Modelling vCards in RDF, 05 Dec 2005.

Extracting vCards from hCard markup

Volume 8, Issue 161; 12 Dec 2005; last modified 08 Oct 2010

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:

# -*- 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#> .
@prefix dc: <http://purl.org/dc/elements/1.1/> .
@prefix cc: <http://web.resource.org/cc/> .

<http://nwalsh.com/rdf/vCard> a owl:Ontology;
    rdfs:comment "Norm's attempt at a vCard/hCard RDF ontology.";
    dc:title "An OWL Ontology for vCards";
    dc:description """This ontology attempts to model a subset of vCards
in RDF using modern (circa 2005) RDF best practices. The subset selected
is the same subset that the microformats community has adopted for use in
hCard.""";
    dc:creator <http://norman.walsh.name/knows/who#norman-walsh>;
    dc:date "2005-12-12";
    dc:rights "Copyright © 2005 Norman Walsh. This work is licensed under the Creative Commons Attribution-NonCommercial License.";
    cc:license <http://creativecommons.org/licenses/by-nc/2.0/> .

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

v:VCard a owl:Class;
    rdfs:subClassOf
        [    a owl:Restriction;
             owl:maxCardinality "1"^^xs:nonNegativeInteger;
             owl:onProperty v:rev ];
    rdfs:label "vCard Class";
    rdfs:comment "Resources that are vCards." .

v:Name a owl:Class;
    rdfs:subClassOf
        [    a owl:Restriction;
             owl:maxCardinality "1"^^xs:nonNegativeInteger;
             owl:onProperty v:family-name ];
    rdfs:subClassOf
        [    a owl:Restriction;
             owl:maxCardinality "1"^^xs:nonNegativeInteger;
             owl:onProperty v:given-name ];
    rdfs:subClassOf
        [    a owl:Restriction;
             owl:maxCardinality "1"^^xs:nonNegativeInteger;
             owl:onProperty v:additional-name ];
    rdfs:subClassOf
        [    a owl:Restriction;
             owl:maxCardinality "1"^^xs:nonNegativeInteger;
             owl:onProperty v:honorific-prefix ];
    rdfs:subClassOf
        [    a owl:Restriction;
             owl:maxCardinality "1"^^xs:nonNegativeInteger;
             owl:onProperty v:honorific-suffix ];
    rdfs:label "vCard Name Class";
    rdfs:comment "Resources that are vCard personal names." .

v:Address a owl:Class;
    rdfs:subClassOf
        [    a owl:Restriction;
             owl:maxCardinality "1"^^xs:nonNegativeInteger;
             owl:onProperty v:post-office-box ];
    rdfs:subClassOf
        [    a owl:Restriction;
             owl:maxCardinality "1"^^xs:nonNegativeInteger;
             owl:onProperty v:extended-address ];
    rdfs:subClassOf
        [    a owl:Restriction;
             owl:maxCardinality "1"^^xs:nonNegativeInteger;
             owl:onProperty v:street-address ];
    rdfs:subClassOf
        [    a owl:Restriction;
             owl:maxCardinality "1"^^xs:nonNegativeInteger;
             owl:onProperty v:locality ];
    rdfs:subClassOf
        [    a owl:Restriction;
             owl:maxCardinality "1"^^xs:nonNegativeInteger;
             owl:onProperty v:region ];
    rdfs:subClassOf
        [    a owl:Restriction;
             owl:maxCardinality "1"^^xs:nonNegativeInteger;
             owl:onProperty v:postal-code ];
    rdfs:subClassOf
        [    a owl:Restriction;
             owl:maxCardinality "1"^^xs:nonNegativeInteger;
             owl:onProperty v:country-name ];
    rdfs:label "vCard Address Class";
    rdfs:comment "Resources that are vCard (postal) addresses." .

v:Organization a owl:Class;
    rdfs:subClassOf
        [    a owl:Restriction;
             owl:maxCardinality "1"^^xs:nonNegativeInteger;
             owl:onProperty v:latitude ];
    rdfs:subClassOf
        [    a owl:Restriction;
             owl:maxCardinality "1"^^xs:nonNegativeInteger;
             owl:onProperty v:longitude ];
    rdfs:label "vCard Organization Class";
    rdfs:comment "Resources that are vCard organizations." .

v:Geo a owl:Class;
    rdfs:subClassOf
        [    a owl:Restriction;
             owl:maxCardinality "1"^^xs:nonNegativeInteger;
             owl:onProperty v:organization-name ];
    rdfs:subClassOf
        [    a owl:Restriction;
             owl:maxCardinality "1"^^xs:nonNegativeInteger;
             owl:onProperty v:organization-unit ];
    rdfs:label "vCard Geographic Location Class";
    rdfs:comment "Resources that are vCard geographic locations." .

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

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

v:n a owl:ObjectProperty;
    rdfs:domain v:VCard;
    rdfs:range v:Name;
    rdfs:label "name";
    rdfs:comment "The components of the name of a person." .

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

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

v:additional-name a owl:DatatypeProperty;
    rdfs:domain v:Name;
    rdfs:label "additional name";
    rdfs:comment "An additional part of a person's name." .

v:honorific-prefix a owl:DatatypeProperty;
    rdfs:domain v:Name;
    rdfs:label "honorific prefix";
    rdfs:comment "An honorific prefix part of a person's name." .

v:honorific-suffix a owl:DatatypeProperty;
    rdfs:domain v:Name;
    rdfs:label "honorific suffix";
    rdfs:comment "An honorific suffix part of a person's name." .

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

v:bday a owl:DatatypeProperty;
    rdfs:domain v:VCard;
    rdfs:range xs:date;
    rdfs:label "birthday";
    rdfs:comment "The birthday of a person." .

v:adr a owl:ObjectProperty;
    rdfs:range v:Address;
    rdfs:label "address";
    rdfs:comment "A postal or street address of a person." .

# A defined set of xxxAdr subproperties; more could be added

v:workAdr a owl:ObjectProperty;
    rdfs:subPropertyOf v:adr;
    rdfs:range v:Address;
    rdfs:label "Work address";
    rdfs:comment "A work address of a person." .

v:homeAdr a owl:ObjectProperty;
    rdfs:subPropertyOf v:adr;
    rdfs:range v:Address;
    rdfs:label "Home address";
    rdfs:comment "A home address of a person." .

v:unlabeledAdr a owl:ObjectProperty;
    rdfs:subPropertyOf v:adr;
    rdfs:range v:Address;
    rdfs:label "address";
    rdfs:comment "An (explicitly) unlabeled address of a person." .

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

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

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

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

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

v:postal-code a owl:DatatypeProperty;
    rdfs:domain v:Address;
    rdfs:label "postal code";
    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:label "country";
    rdfs:comment "The country of a postal address." .

v:label a owl:DatatypeProperty;
    rdfs:domain v:Address;
    rdfs:label "address label";
    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:label "phone";
    rdfs:comment "A telephone number of a person." .

# A defined set of xxxTel subproperties; more could be added

v:workTel a owl:ObjectProperty;
    rdfs:subPropertyOf v:tel;
    rdfs:label "Work phone";
    rdfs:comment "A work phone number of a person." .

v:mobileTel a owl:ObjectProperty;
    rdfs:subPropertyOf v:tel;
    rdfs:label "Mobile phone";
    rdfs:comment "A mobile phone number of a person." .

v:fax a owl:ObjectProperty;
    rdfs:subPropertyOf v:tel;
    rdfs:label "Fax";
    rdfs:comment "A fax number of a person." .

v:homeTel a owl:ObjectProperty;
    rdfs:subPropertyOf v:tel;
    rdfs:label "Home phone";
    rdfs:comment "A home phone number of a person." .

v:unlabeledTel a owl:ObjectProperty;
    rdfs:subPropertyOf v:tel;
    rdfs:label "phone";
    rdfs:comment "An (explicitly) unlabeled phone number of a person." .

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

# A defined set of xxxEmail subproperties; more could be added

v:workEmail a owl:ObjectProperty;
    rdfs:subPropertyOf v:email;
    rdfs:label "Work email";
    rdfs:comment "A work email address of a person." .

v:personalEmail a owl:ObjectProperty;
    rdfs:subPropertyOf v:email;
    rdfs:label "Personal email";
    rdfs:comment "An email address unaffiliated with any particular organization or employer; a personal email address." .

v:mobileEmail a owl:ObjectProperty;
    rdfs:subPropertyOf v:email;
    rdfs:label "Mobile email";
    rdfs:comment "A mobile email address of a person." .

v:unlabeledEmail a owl:ObjectProperty;
    rdfs:subPropertyOf v:email;
    rdfs:label "email";
    rdfs:comment "An (explicitly) unlabeled email address of a person." .

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

v:tz a owl:DatatypeProperty;
    rdfs:domain v:VCard;
    rdfs:label "timezone";
    rdfs:comment "A timezone associated with a person." .

v:geo a owl:ObjectProperty;
    rdfs:domain v:VCard;
    rdfs:range v:Geo;
    rdfs:label "geographic location";
    rdfs:comment "A geographic location associated with a person." .

v:latitude a owl:DatatypeProperty;
    rdfs:domain v:Geo;
    rdfs:range xs:double;
    rdfs:label "latitude";
    rdfs:comment "The latitude of a geographic location." .

v:longitude a owl:DatatypeProperty;
    rdfs:domain v:Geo;
    rdfs:range xs:double;
    rdfs:label "longitude";
    rdfs:comment "The longitude of a geographic location" .

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

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

v:logo a owl:ObjectProperty;
    rdfs:domain v:VCard;
    rdfs:label "logo";
    rdfs:comment "A logo associated with a person or their organization." .

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

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

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

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

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

v:note a owl:DatatypeProperty;
    rdfs:domain v:VCard;
    rdfs:label "notes";
    rdfs:comment "Notes about a person on a vCard." .

v:rev a owl:DatatypeProperty;
    rdfs:domain v:VCard;
    rdfs:range [ owl:unionOf (xs:date xs:dateTime) ];
    rdfs:label "revision";
    rdfs:comment "The timestamp of a revision of a vCard." .

v:sort-string a owl:DatatypeProperty;
    rdfs:domain v:VCard;
    rdfs:label "sort";
    rdfs:comment "A version of a person's name suitable for collation." .

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

v:uid a owl:DatatypeProperty, owl:InverseFunctionalProperty;
    rdfs:domain v:VCard;
    rdfs:label "uid";
    rdfs:comment "A UID of a person's vCard." .

v:url a owl:ObjectProperty;
    rdfs:domain v:VCard;
    rdfs:label "url";
    rdfs:comment "A URL associated with a person." .

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

v:key a owl:ObjectProperty;
    rdfs:domain v:VCard;
    rdfs:label "key";
    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:57 UTC #

Really useful, thanks.

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

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

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

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

—Posted by Dan Connolly on 13 Dec 2005 @ 10:23 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:59 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:13 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:29 UTC #

Comments on this posting are closed. Thank you, spammers.