jCard is now a thing

Last week jCard became rfc7095. jCard specifies a way to serialize the elusive vCard as Json.

I’m a big fan of this format. vCards have been around since 1995, and even though we’ve had a pretty significant update in 2011 in the form of vCard 4.0, the format is still complicated to parse, has a number of problems that go all the way back to the early days.

Worse, a lot of people don’t really see the value of upgrading, and thus stick to using vCard 3. Or even 2.1 if you’re microsoft.

The biggest problem with vCards, is that upon a first glance, the format seems extremely easy to parse and generate with just a couple of string manipulation functions. When you dig deeper into the specifications though, you’ll notice that it’s actually really complex and hosts a ton of edge cases.

The result of this, is that there are a ton of broken vCard generators in the wild, and even more broken vCards.

jCard should solve a lot of that. The serialization rules for json are really simple, and most programming languages ship with a correct implementation. Serializing vCards as json means that there’s no longer a worry about things like escaping, new lines, and character encoding (it’s UTF-8).

So I’m hoping that the benefits of jCard will be big enough for people to eventually abandon vCard altogether. The ‘clean break’ in the format, rather than a progressive update should help ensuring that all newly generated jCards are in a much better state than some of the vCards we’re seeing.

Using it in PHP

sabre/vobject already has support for jCard for some time. To parse jCard:

<?php

$input = file_get_contents('jcard.json');
$jCard = Sabre\VObject\Reader::readJson($input, 'r');

?>

How it looks

Here’s a sample of my vCard (generated by OS X Contacts.app, but significantly shortened).

BEGIN:VCARD
VERSION:3.0
PRODID:-//Apple Inc.//Mac OS X 10.9.1//EN
N:Pot;Evert;;;
FN:Evert Pot
EMAIL;type=INTERNET;type=HOME:evert@rooftopsolutions.nl
EMAIL;type=INTERNET;type=WORK:evert@fruux.com
TEL;type=CELL;type=VOICE:+1 647 471 2661
ADR;type=HOME:;;24 Settles Street;London;;E1 1JP;United Kingdom
NOTE:Foo
item2.URL;type=pref:http://evertpot.com/
BDAY:1985-04-07
X-JABBER;type=HOME;type=pref:evertpot@gmail.com
UID:35269e7016a018e3
END:VCARD

Convering it can be done so, if you have the vObject command line utility installed:

$ vobject convert --pretty input.vcf output.json

What follows is the output, pretty-printed. As you can see the output is a lot larger, and arguably less readable. This is also the one benefit the old mime-dir based format has, it’s easy for a human to process.

[
    "vcard",
    [
        [
            "version",
            {

            },
            "text",
            "4.0"
        ],
        [
            "prodid",
            {

            },
            "text",
            "-\/\/Sabre\/\/Sabre VObject 3.1.3\/\/EN"
        ],
        [
            "n",
            {

            },
            "text",
            [
                "Pot",
                "Evert",
                "",
                "",
                ""
            ]
        ],
        [
            "fn",
            {

            },
            "text",
            "Evert Pot"
        ],
        [
            "email",
            {
                "type": [
                    "INTERNET",
                    "HOME"
                ]
            },
            "text",
            "evert@rooftopsolutions.nl"
        ],
        [
            "email",
            {
                "type": [
                    "INTERNET",
                    "WORK"
                ]
            },
            "text",
            "evert@fruux.com"
        ],
        [
            "tel",
            {
                "type": [
                    "CELL",
                    "VOICE"
                ]
            },
            "text",
            "+1 647 471 2661"
        ],
        [
            "adr",
            {
                "type": "HOME"
            },
            "text",
            [
                "",
                "",
                "24 Settles Street",
                "London",
                "",
                "E1 1JP",
                "United Kingdom"
            ]
        ],
        [
            "note",
            {

            },
            "text",
            "Foo"
        ],
        [
            "url",
            {
                "pref": "1",
                "group": "ITEM2"
            },
            "uri",
            "http:\/\/evertpot.com\/"
        ],
        [
            "bday",
            {

            },
            "date-and-or-time",
            "1985-04-07"
        ],
        [
            "x-jabber",
            {
                "type": "HOME",
                "pref": "1"
            },
            "unknown",
            "evertpot@gmail.com"
        ],
        [
            "uid",
            {

            },
            "text",
            "35269e7016a018e3"
        ]
    ]
]

Web mentions

Comments

  • Brandon Corbin

    <p>Ugh, is it just me or does this seem overly complicated? I was hoping for something more concise and elegant.</p>
    • Evert

      Evert

      <p>Yea it does look a bit complicated ;)</p><p>This was absolutely required for a 1:1 mapping from vCard to jCard. The top-level array couldn't just be an object with properties such as 'N' and 'FN', because almost every one of them can appear more than once.</p><p>Furthermore, many properties can have multiple values, etc.. It really makes sense once you dig into the jCard and vCard formats.</p><p>It doesn't make it very human-readable, but parsing it from within javascript is from experience extremely easy.</p>
      • Brandon Corbin

        <p>Thanks for the details Evert. I am fairly well versed in the vCard format, and have always hated it. Help me understand this - If the objective was to have a clean break from the vCard format, why not just create a better JSON format that can easily be translated? I believe the human readability element is being understated, and we're just passing on the initial inelegant solution to the next evolution.</p>
        • Evert

          Evert

          <p>One of the key objectives was to provide a way to allow a round-trip from vCard to jCard without loss of information, including support for things like custom properties.</p><p>A lot of discussion went into the exact structure of this. An alternative included making the top-level properties (FN, NAME and such) as real object properties, and making their values arrays in cases where they appeared more than once in the original vcard.</p><p>This would have resulted in very deep object structures. Often harder to read, and definitely harder to use when you do actually start using the format.</p><p>Completely departing from the original data model with tight rules around conversion would also have been possible, but this would have either not allowed full round-tripping, or would have added a level of complexity to the format because you need A) a simple object model for common properties, and B) a generic way to encode any custom or future property.</p><p>Basically.. it's hard.. if you have specific ideas on how this should have been done instead, I would very likely be able to point out why that didn't happen (for better or worse).</p>
          • Brandon Corbin

            <p>I have no doubt your solution was the most optimal for the requirements, nor would I be foolish enough to challenge you to a technical dual.</p><p>My issue falls more on the requirement of 1:1 round-tripping without potential loss.</p>
            • Evert

              Evert

              <p>A 'technical dual' was not really my intended tone ;) Just wanted to explain the motivations really.</p><p>I agree, if round-tripping was not a requirement, I imagine the format would have looked very different.</p>
        • David Chang

          <p>We've came across the same issue on a group. This came out as a potential solution:<br><a href="https://github.com/rauchg/rauchg" rel="nofollow noopener" title="https://github.com/rauchg/rauchg">https://github.com/rauchg/r...</a></p>
  • Christophe Coevoet

    <p>@Evert FYI, the permalink is also broken for this article in the RSS feed</p>
    • Evert

      Evert

      <p>It looks like it may have to do with the double // in the feed. Lets hope that that fixes things :)</p>
      • Christophe Coevoet

        <p>your new article works fine in the feed</p>
  • elMestre

    <p>I founded PHP and python libraries for vobject, but not the command line tool.<br>Could you say where its located please?</p>
    • Evert

      Evert

      <p>The commandline tool is in the bin/ directory of the php project. We have no association with the python project.</p>
  • Daniel

    <p>It doesn’t look so bad after Python's pretty-print:</p><p>&gt;&gt;&gt; print pformat(a, width=120).replace("'", '"').replace('\\\\/', '/')<br>["vcard",<br> [["version", {}, "text", "4.0"],<br> ["prodid", {}, "text", "-//Sabre//Sabre VObject 3.1.3//EN"],<br> ["n", {}, "text", ["Pot", "Evert", "", "", ""]],<br> ["fn", {}, "text", "Evert Pot"],<br> ["email", {"type": ["INTERNET", "HOME"]}, "text", "evert@rooftopsolutions.nl"],<br> ["email", {"type": ["INTERNET", "WORK"]}, "text", "evert@fruux.com"],<br> ["tel", {"type": ["CELL", "VOICE"]}, "text", "+1 647 471 2661"],<br> ["adr", {"type": "HOME"}, "text", ["", "", "24 Settles Street", "London", "", "E1 1JP", "United Kingdom"]],<br> ["note", {}, "text", "Foo"],<br> ["url", {"group": "ITEM2", "pref": "1"}, "uri", "<a href="http://evertpot.com/" rel="nofollow noopener" title="http://evertpot.com/">http://evertpot.com/</a>"],<br> ["bday", {}, "date-and-or-time", "1985-04-07"],<br> ["x-jabber", {"pref": "1", "type": "HOME"}, "unknown", "evertpot@gmail.com"],<br> ["uid", {}, "text", "35269e7016a018e3"]]]</p>
  • Sahil

    <p>hey can i find the source code for card anywhere? the parsing from card to j and back</p>
    • Evert

      Evert

      <p>Well there's no source code in my post. I am just talking about the standard.</p><p>I have a PHP implementation that you can check out: <a href="http://sabre.io/vobject/json/" rel="nofollow noopener" title="http://sabre.io/vobject/json/">http://sabre.io/vobject/json/</a></p>