vcard

package module
v0.0.0-...-c52a553 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 27, 2026 License: MIT Imports: 10 Imported by: 0

README

VCard support in Golang

This package provides a simple reflection-based API to encode and decode vCard documents.

  • API is based on json package from standard library.
  • reflect is used to automatically marshal/unmarshal user-defined types and custom fields.
  • supports field renaming using tags.
  • supports custom Schema definition with set of built-in schemas for vCard 4.0, 3.0, 2.1.

This library is still WIP with main concern being performance. Contributions and issues are welcome.

Basic Usage

Read vCard document using io module:

file, _ := os.Open("my_vcard_file.vcf")
data, _ := io.ReadAll(file)
Decode this byte slice into a Go value by providing it by a pointer to vcard.Unmarshal function.

Decoding is only possible into a struct, map or a slice.

// Unmarshal single (or first) vCard document into a map
m := make(map[string]string)
err := vcard.Unmarshal(data, &m)
assert(errors.Is(err, vcard.ErrLeftoverTokens))

// Unmarshal multiple vCard documents into a slice of maps
slice := make([]map[string]string, 0)
err := vcard.Unmarshal(data, &slice)
assert(err == nil)

Decode into a user-defined struct:

type MyUser struct {
    FullName string `vCard:"FN"`
    Name     string `vCard:"N"`
    TEL      string
}
// Unmarshal single (or first) vCard document into a struct
u := MyUser{}
err := vcard.Unmarshal(data, &u)
assert(errors.Is(err, vcard.ErrLeftoverTokens))

// Unmarshal multiple vCard documents into a slice of structs
slice := make([]MyUser, 0)
err := vcard.Unmarshal(data, &slice)
assert(err == nil)
Encode a Go value using vcard.Marshal function.

Encoding is only possible with struct, map or a slice.

// Marshal map m into a byte slice
var m map[string]string
bytes, err := vcard.Marshal(m)

// Marshal struct into a byte slice
var u MyUser
bytes, err := vcard.Marshal(u)

// Marshal a slice of maps into bytes
var slice []map[string]string
bytes, err := vcard.Marshal(slice)

// Marshal a slice of structs into bytes
var slice []MyUser
bytes, err := vcard.Marshal(slice)

Custom encoding logic

VCardFieldMarshaler interface defines custom encoding logic for a single field inside VCard document.

type VCardFieldMarshaler interface {
	MarshalVCardField() ([]byte, error)
}

Let's encode line TEL;TYPE=CELL:(123) 555-5832 in this document:

BEGIN:VCARD
VERSION:4.0
FN:Alex
TEL;TYPE=CELL:(123) 555-5832
END:VCARD
  • TEL is a key
  • ;TYPE=CELL:(123) 555-5832 is a value we need to marshal
type Tel struct {
    typ string
    tel string
}

func (t *Tel) MarshalVCardField() ([]byte, error) {
    // final result is ";TYPE=CELL:(123) 555-5832"
    return fmt.Sprintf(";TYPE=%s:%s", t.typ, t.tel), nil
}

// This struct will use built-in marshaling for FN field and 
// VCardFieldMarshaler implementation for TEL field of type Tel
type MyCustomUser struct {
    FN  string
    TEL Tel
}

Custom decoding logic

VCardFieldUnmarshaler interface defines custom decoding logic for a single field inside VCard document.

type VCardFieldUnmarshaler interface {
	UnmarshalVCardField(data []byte) error
}

Let's decode line TEL;TYPE=CELL:(123) 555-5832 in this document:

BEGIN:VCARD
VERSION:4.0
FN:Alex
TEL;TYPE=CELL:(123) 555-5832
END:VCARD
  • TEL is a key
  • ;TYPE=CELL:(123) 555-5832 is a value we need to unmarshal
type Tel struct {
    typ string
    tel string
}

func (t *Tel) UnmarshalVCardField(data []byte) error {
    // data has format ";TYPE=CELL:(123) 555-5832"
    s := string(data)

    // sl[0] is ";TYPE=CELL"
    // sl[1] is "(123) 555-5832"
    sl := strings.Split(s, ":")
    if len(sl) != 2 {
        return errors.New("Wrong field format")
    }
    if strings.Contains(sl[0], "VOICE") {
        t.typ = "VOICE"
    } else {
        t.typ = "CELL"
    }
    t.tel = sl[1]

    return nil
}

// This struct will use built-in unmarshaling for FN field and
// VCardFieldUnmarshaler implementation for TEL field of type Tel
type MyCustomUser struct {
    FN  string
    TEL Tel
}

Schema definition

Both Encoder and Decoder support defining custom set of schemas for your needs including:

  • Set of fields that need to be serialized. Encoder/Decoder ignore fields outside of this list.
  • Set of required fields. Decoder returns ErrParsing if required field is missing from vCard document. Encoder returns ErrVCard if struct/map is missing required field.
  • You could provide set of schemas with different VERSIONs to Decoder and use single Decoder instance to deal with multiple vCard records of different versions in single file.

This is useful in case of specific requirements, e.g.:

  • TEL field is not required by standard, let's use custom schema to ensure it's presence.
type CustomSchemaV4 struct {
    N     string `vCard:"required"`
    TEL   string `vCard:"required"`
    EMAIL string
}

var MySchemaV4 := SchemaFor[CustomSchemaV4]("4.0")

m := map[string]string {
    "N":     "Alex",
    "EMAIL": "alex@example.com"
    // TEL key is missing
}

b, err := MarshalSchema(m, MySchemaV4)
// err map missing key TEL required by the schema

m := map[string]string {
    "N":     "Alex",
    "EMAIL": "alex@example.com",
    "TEL":   "333 555",

    "HELLO": "World", // HELLO key is not in the schema and will be ignored
}

b, err := MarshalSchema(m, MySchemaV4)

// resulting b is:
//
// BEGIN:VCARD
// VERSION:4.0
// N:Alex
// EMAIL:alex@example.com
// TEL:333 555
// END:VCARD

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DefaultSchemas = []Schema{
	SchemaV4,
	SchemaV3,
	SchemaV2_1,
}

Default set of schemas used by Decoder

View Source
var ErrLeftoverTokens = fmt.Errorf("%w: leftover tokens", ErrParsing)

Signifies decoding was successful but there are more tokens left. This could be the case when trying to decode a document of multiple vCards into a single struct or a map.

View Source
var ErrParsing = fmt.Errorf("%w: parsing error in Decoder", ErrVCard)

Signifies decoding was unsuccessful because of the syntax of the vCard document and not more significant errors.

View Source
var ErrVCard = errors.New("vCard")

Signifies any error from vCard library

View Source
var SchemaV2_1 = SchemaFor[StringSchemaV2_1]("2.1")

Simple vCard 2.1 schema

View Source
var SchemaV3 = SchemaFor[StringSchemaV3]("3.0")

Simple vCard 3.0 schema

View Source
var SchemaV4 = SchemaFor[StringSchemaV4]("4.0")

Simple vCard 4.0 schema

Functions

func Marshal

func Marshal(v any) ([]byte, error)

Serializes a Go value as a vCard document using default vCard 4.0 schema.

v has to be a map, struct or a slice.

func MarshalSchema

func MarshalSchema(v any, schema Schema) ([]byte, error)

Serializes a Go value as a vCard document using provided Schema.

v has to be a map, struct or a slice.

func Unmarshal

func Unmarshal(data []byte, v any) error

Deserializes a vCard document into a Go value using default set of [Schema]s.

v has to be a pointer to a slice, struct or a map.

func UnmarshalSchema

func UnmarshalSchema(data []byte, v any, schemas []Schema) error

Deserializes a vCard document into a Go value using provided set of [Schema]s.

v has to be a pointer to a slice, struct or a map.

Types

type Decoder

type Decoder struct {
	// contains filtered or unexported fields
}

Reads a vCard document from an input stream.

func NewDecoder

func NewDecoder(r io.Reader, schemas []Schema) *Decoder

Creates new Decoder that reads from r using provided schemas.

panics if schemas slice has multiple schemas with same version. if schemas slice is empty

func (*Decoder) Decode

func (d *Decoder) Decode(v any) error

Decodes a vCard document into pointer v using provided schema.

Returns ErrParsing in case of a malformed vCard document recived from Writer.

v has to be a pointer to a struct, map or a slice.

func (*Decoder) SetSmartStrings

func (d *Decoder) SetSmartStrings(smartStrings bool) *Decoder

Toggles smart string encoding. Enabled by default.

In smart mode, decoder checks at runtime if string starts with `:` (part of KEY:VALUE separator) and removes it if neccesary e.g. string fields will contain "Alex" instead of ":Alex".

See Encoder.SetSmartStrings for more info.

type Encoder

type Encoder struct {
	// contains filtered or unexported fields
}

Writes a vCard document to an output stream.

func NewEncoder

func NewEncoder(w io.Writer) *Encoder

Creates new Encoder that writes to w.

func (*Encoder) Encode

func (e *Encoder) Encode(v any) error

Writes a vCard representation of v to the stream using default vCard 4.0 schema.

fields of v have to either match the name and the type from the schema or implement Marshaler for custom encoding logic.

func (*Encoder) EncodeSchema

func (e *Encoder) EncodeSchema(v any, schema Schema) error

Writes a vCard representation of v to the stream using provided Schema.

v has to be a struct, slice of structs, map or slice of maps. Map key has to be a string. Map value has to either implement VCardFieldMarshaler or be one of the supported types, e.g. a string.

func (*Encoder) SetNewlineSequence

func (e *Encoder) SetNewlineSequence(seq string) *Encoder

Sets newline sequence. Defaults to "\r\n" as per vCard RFC https://datatracker.ietf.org/doc/html/rfc6350#section-3.2

func (*Encoder) SetSmartStrings

func (e *Encoder) SetSmartStrings(smartStrings bool) *Encoder

Toggles smart string encoding. Enabled by default.

In smart mode, encoder checks at runtime if string contains `:` (KEY:VALUE separator) and adds it if neccesary. This is useful because some fields have more complex format e.g.:

For string "N:;Alex;;;" k="N", v=";Alex;;;" - `:` won't be a part of an encoded value.

For string "TEL;TYPE=CELL:555" k="TEL", v=";TYPE=CELL:555" - `:` will be in the middle of an encoded value.

Disabling smart strings encoding will increase performance, but you have to ensure your strings have proper puctuation in them e.g. you will have to deal with ":Name" instead of "Name".

You can overcome this limitation by using custom fields implementing VCardFieldMarshaler instead of string.

type Schema

type Schema struct {
	// contains filtered or unexported fields
}

Struct used for schema definition. See StringSchemaV4 as an example.

vCard schema may differ slightly between applications e.g. Android vs Outlook.

Note that in .vcf specification each record may have different schema depending on it's version, which means you can provide multiple schemas to Decoder.

Built-in schemas are based on https://en.wikipedia.org/wiki/VCard so it is recommended to provide custom set of schemas e.g. if TEL field is required in your case.

func NewSchema

func NewSchema(version string, fields []string, requiredFields []string) Schema

Creates a new schema from slice of fields and required fields

func SchemaFor

func SchemaFor[T any](version string) Schema

Creates a schema for any struct. See StringSchemaV4 as an example.

Use tag `vCard:"required"` on a field to make Encoder and Decoder return errors in case a field was not found.

type StringSchemaV2_1

type StringSchemaV2_1 struct {
	ADR         string // A structured representation of the delivery address for the person.
	AGENT       string // Information about another person who will act on behalf of this one.
	ANNIVERSARY string // Defines the person's anniversary.
	BDAY        string // Date of birth of the individual.

	CALADRURI    string // A URL to use for sending a scheduling request to the person's calendar.
	CALURI       string // A URL to the person's calendar.
	CATEGORIES   string // A list of "tags" that can be used to describe the person.
	CLASS        string // Describes the sensitivity of the information in the vCard.
	CLIENTPIDMAP string // Used for synchronizing different revisions of the same vCard.
	EMAIL        string // The address for electronic mail communication

	FBURL  string // Defines a URL that shows when the person is "free" or "busy" on their calendar.
	FN     string // The formatted name string.
	GENDER string // Defines the person's gender.
	GEO    string // Specifies a latitude and longitude.
	IMPP   string // Defines an instant messenger handle.
	KEY    string // The public encryption key associated with the person.
	KIND   string // Defines the type of entity that this vCard represents.

	// Represents the actual text that should be put on the mailing label. Not supported in version 4.0.
	// Instead, this information is stored in the LABEL parameter of the ADR property.
	LABEL string

	LANG   string // Defines a language that the person speaks.
	MAILER string // Type of email program used.
	MEMBER string // Defines a member that is part of the group that this vCard represents.
	N      string `vCard:"required"` // A structured representation of the name of the person

	// Provides a textual representation of the SOURCE property. Not to be confused with N property
	// which defines person's name.
	NAME string

	NICKNAME string // One or more descriptive/familiar names.
	NOTE     string // Comment that is associated with the person.
	ORG      string // The name and optionally the unit(s) of the organization associated with the person.

	PHOTO   string // An image of the individual. It may point to an external URL or may be embedded as a Base64.
	PRODID  string // The identifier for the product that created the vCard object.
	PROFILE string // States that the vCard is a vCard.

	RELATED string // Another entity that the person is related to.
	REV     string // A timestamp for the last time the vCard was updated.
	ROLE    string // The role, occupation, or business category of the person within an organization.

	// Defines a string that should be used when an application sorts this vCard in some way.
	// Not supported in 4.0. Instead, this is stored in the SORT-AS parameter of the N and/or ORG properties.
	SORT_STRING string

	SOUND  string // Specifies the pronunciation of the FN property.
	SOURCE string // A URL that can be used to get the latest version of this vCard.
	TEL    string // The canonical number string for a telephone number.

	TITLE string // Specifies the job title, functional position or function of the individual.
	TZ    string // The time zone of the person.
	UID   string // Specifies a persistent, globally unique identifier associated with the person.
	URL   string // A URL pointing to a website that represents the person in some way.

	// VERSION field is special. It does not have `vCard:"required"` annotation but still
	// considered required by [Decoder]. This was done for the case of user-defined struct
	// not having VERSION field or field tagges `vCard:"VERSION"`. In this case [Decoder] will
	// not return an error.
	VERSION string // The version of the vCard specification.

	XML string // Any XML data that is attached to the vCard.
}

Simple vCard v2.1 schema implementation from https://en.wikipedia.org/wiki/VCard

Note that this struct can be used as argument in vCard.Unmarshal without the need to provide user-defined type.

The difference between v2.1 and v3.0 is that FN property is optional.

type StringSchemaV3

type StringSchemaV3 struct {
	ADR         string // A structured representation of the delivery address for the person.
	AGENT       string // Information about another person who will act on behalf of this one.
	ANNIVERSARY string // Defines the person's anniversary.
	BDAY        string // Date of birth of the individual.

	CALADRURI    string // A URL to use for sending a scheduling request to the person's calendar.
	CALURI       string // A URL to the person's calendar.
	CATEGORIES   string // A list of "tags" that can be used to describe the person.
	CLASS        string // Describes the sensitivity of the information in the vCard.
	CLIENTPIDMAP string // Used for synchronizing different revisions of the same vCard.
	EMAIL        string // The address for electronic mail communication

	FBURL  string // Defines a URL that shows when the person is "free" or "busy" on their calendar.
	FN     string `vCard:"required"` // The formatted name string.
	GENDER string // Defines the person's gender.
	GEO    string // Specifies a latitude and longitude.
	IMPP   string // Defines an instant messenger handle.
	KEY    string // The public encryption key associated with the person.
	KIND   string // Defines the type of entity that this vCard represents.

	// Represents the actual text that should be put on the mailing label. Not supported in version 4.0.
	// Instead, this information is stored in the LABEL parameter of the ADR property.
	LABEL string

	LANG   string // Defines a language that the person speaks.
	MAILER string // Type of email program used.
	MEMBER string // Defines a member that is part of the group that this vCard represents.
	N      string `vCard:"required"` // A structured representation of the name of the person

	// Provides a textual representation of the SOURCE property. Not to be confused with N property
	// which defines person's name.
	NAME string

	NICKNAME string // One or more descriptive/familiar names.
	NOTE     string // Comment that is associated with the person.
	ORG      string // The name and optionally the unit(s) of the organization associated with the person.

	PHOTO   string // An image of the individual. It may point to an external URL or may be embedded as a Base64.
	PRODID  string // The identifier for the product that created the vCard object.
	PROFILE string // States that the vCard is a vCard.

	RELATED string // Another entity that the person is related to.
	REV     string // A timestamp for the last time the vCard was updated.
	ROLE    string // The role, occupation, or business category of the person within an organization.

	// Defines a string that should be used when an application sorts this vCard in some way.
	// Not supported in 4.0. Instead, this is stored in the SORT-AS parameter of the N and/or ORG properties.
	SORT_STRING string

	SOUND  string // Specifies the pronunciation of the FN property.
	SOURCE string // A URL that can be used to get the latest version of this vCard.
	TEL    string // The canonical number string for a telephone number.

	TITLE string // Specifies the job title, functional position or function of the individual.
	TZ    string // The time zone of the person.
	UID   string // Specifies a persistent, globally unique identifier associated with the person.
	URL   string // A URL pointing to a website that represents the person in some way.

	// VERSION field is special. It does not have `vCard:"required"` annotation but still
	// considered required by [Decoder]. This was done for the case of user-defined struct
	// not having VERSION field or field tagges `vCard:"VERSION"`. In this case [Decoder] will
	// not return an error.
	VERSION string // The version of the vCard specification.

	XML string // Any XML data that is attached to the vCard.
}

Simple vCard v3.0 schema implementation from https://en.wikipedia.org/wiki/VCard

Note that this struct can be used as argument in vCard.Unmarshal without the need to provide user-defined type.

The difference between v4.0 and v3.0 is that N property is required to be set.

type StringSchemaV4

type StringSchemaV4 struct {
	ADR         string // A structured representation of the delivery address for the person.
	AGENT       string // Information about another person who will act on behalf of this one.
	ANNIVERSARY string // Defines the person's anniversary.
	BDAY        string // Date of birth of the individual.

	CALADRURI    string // A URL to use for sending a scheduling request to the person's calendar.
	CALURI       string // A URL to the person's calendar.
	CATEGORIES   string // A list of "tags" that can be used to describe the person.
	CLASS        string // Describes the sensitivity of the information in the vCard.
	CLIENTPIDMAP string // Used for synchronizing different revisions of the same vCard.
	EMAIL        string // The address for electronic mail communication

	FBURL  string // Defines a URL that shows when the person is "free" or "busy" on their calendar.
	FN     string `vCard:"required"` // The formatted name string.
	GENDER string // Defines the person's gender.
	GEO    string // Specifies a latitude and longitude.
	IMPP   string // Defines an instant messenger handle.
	KEY    string // The public encryption key associated with the person.
	KIND   string // Defines the type of entity that this vCard represents.

	// Represents the actual text that should be put on the mailing label. Not supported in version 4.0.
	// Instead, this information is stored in the LABEL parameter of the ADR property.
	LABEL string

	LANG   string // Defines a language that the person speaks.
	MAILER string // Type of email program used.
	MEMBER string // Defines a member that is part of the group that this vCard represents.
	N      string // A structured representation of the name of the person

	// Provides a textual representation of the SOURCE property. Not to be confused with N property
	// which defines person's name.
	NAME string

	NICKNAME string // One or more descriptive/familiar names.
	NOTE     string // Comment that is associated with the person.
	ORG      string // The name and optionally the unit(s) of the organization associated with the person.

	PHOTO   string // An image of the individual. It may point to an external URL or may be embedded as a Base64.
	PRODID  string // The identifier for the product that created the vCard object.
	PROFILE string // States that the vCard is a vCard.

	RELATED string // Another entity that the person is related to.
	REV     string // A timestamp for the last time the vCard was updated.
	ROLE    string // The role, occupation, or business category of the person within an organization.

	// Defines a string that should be used when an application sorts this vCard in some way.
	// Not supported in 4.0. Instead, this is stored in the SORT-AS parameter of the N and/or ORG properties.
	SORT_STRING string

	SOUND  string // Specifies the pronunciation of the FN property.
	SOURCE string // A URL that can be used to get the latest version of this vCard.
	TEL    string // The canonical number string for a telephone number.

	TITLE string // Specifies the job title, functional position or function of the individual.
	TZ    string // The time zone of the person.
	UID   string // Specifies a persistent, globally unique identifier associated with the person.
	URL   string // A URL pointing to a website that represents the person in some way.

	// VERSION field is special. It does not have `vCard:"required"` annotation but still
	// considered required by [Decoder]. This was done for the case of user-defined struct
	// not having VERSION field or field tagges `vCard:"VERSION"`. In this case [Decoder] will
	// not return an error.
	VERSION string // The version of the vCard specification.

	XML string // Any XML data that is attached to the vCard.
}

Simple vCard v4.0 schema implementation from https://en.wikipedia.org/wiki/VCard

Note that this struct can be safely used as argument in vCard.Unmarshal without the need to provide user-defined type.

The difference between v4.0 and v3.0 is that N property is optional.

type VCardFieldMarshaler

type VCardFieldMarshaler interface {
	MarshalVCardField() ([]byte, error)
}

Implemented by fields that need custom Marshaling logic.

Note that this interface defines a way to marshal a value of single field. e.g. TEL field has custom type Tel:

type Tel struct {
	typ string
	tel string
}

func (t *Tel) MarshalVCardField() ([]byte, error) {
	// final result is TEL;TYPE=CELL:(123) 555-5832
	return fmt.Sprintf(";TYPE=%s:%s", t.typ, t.tel), nil
}

type VCardFieldUnmarshaler

type VCardFieldUnmarshaler interface {
	UnmarshalVCardField(data []byte) error
}

Implemented by fields that need custom Unmarshaling logic.

Note that this interface defines a way to unmarshal single field. e.g. TEL field has custom type Tel:

type Tel struct {
	typ string
	tel string
}

func (t *Tel) UnmarshalVCardField(data []byte) error {
	// data has a form of ";TYPE=CELL:(123) 555-5832"
	s := string(data)

	sl := strings.Split(s, ":")
	if len(sl) != 2 {
		return errors.New("Unable to unmarshal")
	}

	if strings.Contains(sl[0], "VOICE") {
		t.typ = "VOICE"
	} else {
		t.typ = "CELL"
	}
	t.tel = sl[1]

	return nil
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL