Windows Phone: Parse a VCard String

2019-06-09 22:48发布

问题:

I have a VCard String that I want to parse and insert to the phone's contacts store. I checked this link http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj207024(v=vs.105).aspx#BKMK_UsingtheRemoteIdHelperclass

and found the following method

ContactInformation info = await ContactInformation.ParseVcardAsync(IInputStream vcard);

how can I convert my VCard String to an IInputStream so that I can parse it ?

Thanks

回答1:

I Used this class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Collections.Specialized;
using System.Collections.ObjectModel;
using System.Text.RegularExpressions;

namespace aaa
{
    /// <summary>
    /// Container class for vCard properties of vCard 2.1.
    /// Considering current implementation of vCard support in MS Outlook, MS Outlook Express, Yahoo Mail and Eudora etc.,
    /// the following properties and attributes are not supported:
    /// * Photo
    /// * Address labels
    /// * Delivery address types
    /// * Mailer
    /// * Timezone
    /// * EMail types
    /// * Sound
    /// * Public key
    /// * Extensions
    /// * Geo
    /// 
    /// So this vCard implementation is expected to be compatible with standard vCard fields of the vCard generated by these programs.
    /// </summary>
    public class VCard
    {
        readonly char[] lineBreak = { '\n', '\r' };
        #region Singlar Properties

        private string formattedName;

        public string FormattedName
        {
            get { return formattedName; }
            set { formattedName = value.TrimEnd(lineBreak); }
        }

        string surname;
        public string Surname
        {
            get { return surname; }
            set { surname = value.TrimEnd(lineBreak); }
        }

        private string givenName;

        public string GivenName
        {
            get { return givenName; }
            set { givenName = value.TrimEnd(lineBreak); }
        }
        private string middleName;

        public string MiddleName
        {
            get { return middleName; }
            set { middleName = value.TrimEnd(lineBreak); }
        }

        private string prefix;

        public string Prefix
        {
            get { return prefix; }
            set { prefix = value.TrimEnd(lineBreak); }
        }
        private string suffix;

        public string Suffix
        {
            get { return suffix; }
            set { suffix = value.TrimEnd(lineBreak); }
        }

        private string title;

        public string Title
        {
            get { return title; }
            set { title = value.TrimEnd(lineBreak); }
        }

        private DateTime bday;

        public DateTime Birthday
        {
            get { return bday; }
            set { bday = value; }
        }

        private DateTime rev = DateTime.MinValue;
        /// <summary>
        /// If Rev in vCard is UTC, Rev will convert utc to local datetime.
        /// </summary>
        public DateTime Rev
        {
            get { return rev; }
            set { rev = value; }
        }

        private string org;

        public string Org
        {
            get { return org; }
            set { org = value.TrimEnd(lineBreak); }
        }

        private string department;

        public string Department
        {
            get { return department; }
            set { department = value.TrimEnd(lineBreak); }
        }


        private string note;

        public string Note
        {
            get { return note; }
            set { note = value.TrimEnd(lineBreak); }
        }

        private string role;

        public string Role
        {
            get { return role; }
            set { role = value.TrimEnd(lineBreak); }
        }


        #endregion


        #region Property Collections with attribute

        private Collection<Address> addresses = new Collection<Address>();

        public Collection<Address> Addresses
        {
            get { return addresses; }
            set { addresses = value; }
        }

        private Collection<PhoneNumber> phones = new Collection<PhoneNumber>();

        public Collection<PhoneNumber> Phones
        {
            get { return phones; }
            set { phones = value; }
        }

        private Collection<EmailAddress> emails = new Collection<EmailAddress>();

        public Collection<EmailAddress> Emails
        {
            get { return emails; }
            set { emails = value; }
        }

        private Collection<URL> urls = new Collection<URL>();

        public Collection<URL> URLs
        {
            get { return urls; }
            set { urls = value; }
        }


        #endregion

    }
    /// <summary>
    /// Read text and create data fields of collections.
    /// 
    /// When testing, please be aware the following facts:
    /// vCard standard has been around for more than 10 years, and widely accpeted by the industries. However,
    /// the implementations from different vendors are a bit buggy, resulting data corruption during data exchanges.
    /// For example
    /// 1. MS Outlook 2003 can handle Unicode. When exporting to vCard, non-ASCII characters are encoded into QuotedPrintable over UTF8,
    /// however, when importing, Outlook will fail to import those characters.
    /// 2. Yahoo has similar problem. In additon, Rev in Yahoo vCard is not DateTime, a bit doggy.
    /// 
    /// These applications just can not eat their own dog food. So when you are testing the exchanges of vCard, keep these facts in mind.
    /// </summary>
    public class VCardReader
    {
        const string regxLine = @"((?<strElement>[^\;^:]*) ((;(ENCODING=)?(?<strAttr>(QUOTED-PRINTABLE)))|(;CHARSET=UTF-?8)|(;[\w]*))*  (:(?<strValue> (([^\n\r]*=[\n\r]+)*[^\n\r]*[^=][\n\r]*) )))";
        const string regxN = @"(?<strElement>(N)) ((;(ENCODING=)?(?<strAttr>(QUOTED-PRINTABLE)))|(;CHARSET=UTF-?8))* (:(?<strSurname>(([^;^\n\r]*(=\n\r)?)*[^;^\n\r]*[^;]*(\n\r)?) ))(;(?<strGivenName>(([^;^\n\r]*(=\n\r)?)*[^;^\n\r]*[^;]*(\n\r)?) ))? (;(?<strMidName>(([^;^\n\r]*(=\n\r)?)*[^;^\n\r]*[^;]*(\n\r)?) ))? (;(?<strPrefix>(([^;^\n\r]*(=\n\r)?)*[^;^\n\r]*[^;]*(\n\r)?) ))? (;(?<strSuffix>(([^;^\n\r]*(=\n\r)?)*[^;^\n\r]*[^;]*(\n\r)?) ))?";
        const string regxFN = @"(?<strElement>(FN))((;(ENCODING=)?(?<strAttr>(QUOTED-PRINTABLE)))|(;CHARSET=UTF-?8))* (:(?<strFN>(([^\n\r]*=[\n\r]+)*[^\n\r]*[^=][\n\r]*) ))";
        const string regxTitle = @"(?<strElement>(TITLE))((;(ENCODING=)?(?<strAttr>(QUOTED-PRINTABLE)))|(;CHARSET=UTF-?8))* (:(?<strTITLE>[^\n\r]*))";
        const string regxOrg = @"(?<strElement>(ORG)) ((;(ENCODING=)?(?<strAttr>(QUOTED-PRINTABLE)))|(;CHARSET=UTF-?8))*  (:(?<strORG>(([^;^\n\r]*(=\n\r)?)*[^;^\n\r]*[^;]*(\n\r)?) ))(;(?<strDept>(([^;^\n\r]*(=\n\r)?)*[^;^\n\r]*[^;]*(\n\r)?) ))?";
        const string regxAdr = @"(?<strElement>(ADR)) (;(?<strAttr>(HOME|WORK)))?((;(ENCODING=)?(?<strAttr>(QUOTED-PRINTABLE)))|(;CHARSET=UTF-?8))*(:(?<strPo>([^;]*)))  (;(?<strBlock>([^;]*)))  (;(?<strStreet>([^;]*)))  (;(?<strCity>([^;]*))) (;(?<strRegion>([^;]*))) (;(?<strPostcode>([^;]*)))(;(?<strNation>(([^;^\n\r]*(=\n\r)?)*[^;^\n\r]*[^;]*(\n\r)?) ))";
        const string regxNote = @"((?<strElement>(NOTE)) ((;CHARSET=UTF-?8)|(;(ENCODING=)?(?<strAttr>(QUOTED-PRINTABLE))))*   (:(?<strValue> (([^\n\r]*=[\n\r]+)*[^\n\r]*[^=][\n\r]*) )))";
        const string regxRole = @"(?<strElement>(ROLE)) ((;(ENCODING=)?(?<strAttr>(QUOTED-PRINTABLE)))|(;CHARSET=UTF-?8))*  (:(?<strROLE>(([^\n\r]*=[\n\r]+)*[^\n\r]*[^=][\n\r]*) ))";
        /// <summary>
        /// Analyze vCard text into vCard properties.
        /// </summary>
        /// <param name="vCardText">vCard text.</param>
        /// <returns>vCard object.</returns>
        public static VCard ParseText(string vCardText)
        {
            VCard v = new VCard();
            RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace;

            Regex regex;
            Match m;
            MatchCollection mc;

          //  NameValueCollection vCardLines = new NameValueCollection();
            regex = new Regex(regxLine, options);
            MatchCollection matches = regex.Matches(vCardText);

            foreach (Match match in matches)
            {
                string ss;

                string vCardLine = match.Value;
                switch (match.Groups["strElement"].Value)
                {
                    case "FN":
                        regex = new Regex(regxFN, options);
                        m = regex.Match(vCardLine);
                        if (m.Success)
                        {
                            if (m.Groups["strAttr"].Value == "QUOTED-PRINTABLE")
                                v.FormattedName = QuotedPrintable.Decode(m.Groups["strFN"].Value);
                            else
                                v.FormattedName = m.Groups["strFN"].Value;
                        }
                        break;
                    case "N":
                        regex = new Regex(regxN, options);
                        m = regex.Match(vCardLine);
                        if (m.Success)
                        {
                            if (m.Groups["strAttr"].Value == "QUOTED-PRINTABLE")
                            {
                                v.Surname = QuotedPrintable.Decode(m.Groups["strSurname"].Value);
                                v.GivenName = QuotedPrintable.Decode(m.Groups["strGivenName"].Value);
                                v.MiddleName = QuotedPrintable.Decode(m.Groups["strMidName"].Value);
                                v.Prefix = QuotedPrintable.Decode(m.Groups["strPrefix"].Value);
                                v.Suffix = QuotedPrintable.Decode(m.Groups["strSuffix"].Value);
                            }
                            else
                            {
                                v.Surname = m.Groups["strSurname"].Value;
                                v.GivenName = m.Groups["strGivenName"].Value;
                                v.MiddleName = m.Groups["strMidName"].Value;
                                v.Prefix = m.Groups["strPrefix"].Value;
                                v.Suffix = m.Groups["strSuffix"].Value;
                            }

                        }
                        break;
                    case "TITLE":
                        regex = new Regex(regxTitle, options);
                        m = regex.Match(vCardLine);
                        if (m.Success)
                        {
                            if (m.Groups["strAttr"].Value == "QUOTED-PRINTABLE")
                                v.Title = QuotedPrintable.Decode(m.Groups["strTITLE"].Value);
                            else
                                v.Title = m.Groups["strTITLE"].Value;
                        }
                        break;
                    case "ORG":
                        regex = new Regex(regxOrg, options);
                        m = regex.Match(vCardLine);
                        if (m.Success)
                        {
                            if (m.Groups["strAttr"].Value == "QUOTED-PRINTABLE")
                            {
                                v.Org = QuotedPrintable.Decode(m.Groups["strORG"].Value);
                                v.Department = QuotedPrintable.Decode(m.Groups["strDept"].Value);
                            }
                            else
                            {
                                v.Org = m.Groups["strORG"].Value;
                                v.Department = m.Groups["strDept"].Value;
                            }

                        }
                        break;
                    case "BDAY":
                        regex = new Regex(@"(?<strElement>(BDAY))   (:(?<strBDAY>[^\n\r]*))", options);
                        m = regex.Match(vCardLine);
                        if (m.Success)
                        {
                            string bdayStr = m.Groups["strBDAY"].Value;
                            if (!String.IsNullOrEmpty(bdayStr))
                            {
                                string[] expectedFormats = { "yyyyMMdd", "yyMMdd", "yyyy-MM-dd" };
                                v.Birthday = DateTime.ParseExact(bdayStr, expectedFormats, null, System.Globalization.DateTimeStyles.AllowWhiteSpaces);
                            }
                        }
                        break;
                    case "REV":
                        regex = new Regex(@"(?<strElement>(REV)) (;CHARSET=utf-8)?  (:(?<strREV>[^\n\r]*))", options);
                        m = regex.Match(vCardLine);
                        if (m.Success)
                        {
                            string revStr = m.Groups["strREV"].Value;
                            if (!String.IsNullOrEmpty(revStr))
                            {
                                string[] expectedFormats = { "yyyyMMddHHmmss", "yyyyMMddTHHmmssZ" };
                                v.Rev = DateTime.ParseExact(revStr, expectedFormats, null, System.Globalization.DateTimeStyles.AllowWhiteSpaces);
                            }
                        }
                        break;
                    case "EMAIL":
                        regex = new Regex(@"((?<strElement>(EMAIL)) ((;(?<strPref>(PREF))))* (;[^:]*)*  (:(?<strValue>[^\n\r]*)))", options);
                        mc = regex.Matches(vCardLine);
                        if (mc.Count > 0)
                        {
                            for (int i = 0; i < mc.Count; i++)
                            {
                                EmailAddress email = new EmailAddress();
                                v.Emails.Add(email);
                                m = mc[i];
                                email.Address = m.Groups["strValue"].Value;

                                if (m.Groups["strPref"].Value == "PREF")
                                    email.Pref = true;
                            }
                        }

                        break;
                    case "TEL":
                        regex = new Regex(@"((?<strElement>(TEL))  ((;(?<strType>(VOICE|CELL|PAGER|MSG|FAX)))| (;(?<strAttr>(HOME|WORK)))| (;(?<strPref>(PREF)))?)*  (:(?<strValue>[^\n\r]*)))", options);
                        mc = regex.Matches(vCardLine);
                        if (mc.Count > 0)
                        {
                            for (int i = 0; i < mc.Count; i++)
                            {
                                PhoneNumber phone = new PhoneNumber();
                                v.Phones.Add(phone);
                                m = mc[i];
                                phone.Number = m.Groups["strValue"].Value;
                                ss = m.Groups["strAttr"].Value;
                                if (ss == "HOME")
                                    phone.HomeWorkTypes = HomeWorkTypes.HOME;
                                else if (ss == "WORK")
                                    phone.HomeWorkTypes = HomeWorkTypes.WORK;

                                if (m.Groups["strPref"].Value == "PREF")
                                    phone.Pref = true;

                                CaptureCollection types = m.Groups["strType"].Captures;
                                foreach (Capture capture in types)
                                {
                                    switch (capture.Value)
                                    {
                                        case "VOICE":
                                            phone.PhoneTypes |= PhoneTypes.VOICE;
                                            break;
                                        case "CELL": phone.PhoneTypes |= PhoneTypes.CELL;
                                            break;
                                        case "PAGER": phone.PhoneTypes |= PhoneTypes.PAGER;
                                            break;
                                        case "MSG": phone.PhoneTypes |= PhoneTypes.MSG;
                                            break;
                                        case "FAX": phone.PhoneTypes |= PhoneTypes.FAX;
                                            break;
                                    }
                                }

                            }
                        }
                        break;
                    case "ADR":
                        regex = new Regex(regxAdr, options);
                        mc = regex.Matches(vCardLine);
                        if (mc.Count > 0)
                        {
                            for (int i = 0; i < mc.Count; i++)
                            {
                                Address address = new Address();
                                v.Addresses.Add(address);
                                m = mc[i];
                                ss = m.Groups["strAttr"].Value;
                                if (ss == "HOME")
                                    address.HomeWorkType = HomeWorkTypes.HOME;
                                else if (ss == "WORK")
                                    address.HomeWorkType = HomeWorkTypes.WORK;

                                if (m.Groups["strAttr"].Value == "QUOTED-PRINTABLE")
                                {
                                    address.POBox = QuotedPrintable.Decode(m.Groups["strPo"].Value);
                                    address.Ext = QuotedPrintable.Decode(m.Groups["strBlock"].Value);
                                    address.Street = QuotedPrintable.Decode(m.Groups["strStreet"].Value);
                                    address.Locality = QuotedPrintable.Decode(m.Groups["strCity"].Value);
                                    address.Region = QuotedPrintable.Decode(m.Groups["strRegion"].Value);
                                    address.Postcode = QuotedPrintable.Decode(m.Groups["strPostcode"].Value);
                                    address.Country = QuotedPrintable.Decode(m.Groups["strNation"].Value);
                                }
                                else
                                {
                                    address.POBox = m.Groups["strPo"].Value;
                                    address.Ext = m.Groups["strBlock"].Value;
                                    address.Street = m.Groups["strStreet"].Value;
                                    address.Locality = m.Groups["strCity"].Value;
                                    address.Region = m.Groups["strRegion"].Value;
                                    address.Postcode = m.Groups["strPostcode"].Value;
                                    address.Country = m.Groups["strNation"].Value;
                                }

                            }
                        }
                        break;
                    case "NOTE":
                        regex = new Regex(regxNote, options);
                        m = regex.Match(vCardLine);
                        if (m.Success)
                        {
                            if (m.Groups["strAttr"].Value == "QUOTED-PRINTABLE")
                                v.Note = QuotedPrintable.Decode(m.Groups["strValue"].Value);
                            else
                                v.Note = m.Groups["strValue"].Value;
                        }
                        break;
                    case "URL":
                        regex = new Regex(@"((?<strElement>(URL)) (;*(?<strAttr>(HOME|WORK)))?   (:(?<strValue>[^\n\r]*)))", options);
                        mc = regex.Matches(vCardLine);
                        if (mc.Count > 0)
                        {
                            for (int i = 0; i < mc.Count; i++)
                            {
                                URL url = new URL();
                                v.URLs.Add(url);
                                m = mc[i];
                                url.Address = m.Groups["strValue"].Value;
                                ss = m.Groups["strAttr"].Value;
                                if (ss == "HOME")
                                    url.HomeWorkTypes = HomeWorkTypes.HOME;
                                else if (ss == "WORK")
                                    url.HomeWorkTypes = HomeWorkTypes.WORK;
                            }
                        }

                        break;
                    case "ROLE":
                        regex = new Regex(regxRole, options);
                        m = regex.Match(vCardLine);
                        if (m.Success)
                            v.Role = m.Groups["strROLE"].Value;
                        break;


                }
            }

            return v;
        }



    }
}

with this code

    using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using Microsoft.Phone.Tasks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;

namespace aaa
{

    public partial class SaveContactPage : PhoneApplicationPage
    {
        string vcard = "";

        SaveContactTask saveContactTask = new SaveContactTask();

        public SaveContactPage()
        {
            InitializeComponent();
            saveContactTask.Completed += new EventHandler<SaveContactResult>(saveContactTask_Completed);
        }

        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);
            if (NavigationContext.QueryString.TryGetValue("vcard", out vcard))
            {

                TextBox.Text = "";
                VCard vc = VCardReader.ParseText(vcard);

                saveContactTask.FirstName = vc.GivenName;
                saveContactTask.LastName = vc.Surname;

                TextBox.Text += "FullName: ";
                TextBox.Text += saveContactTask.FirstName + saveContactTask.LastName;
                TextBox.Text += Environment.NewLine;

                for (int i = 0; i < vc.Phones.Count; i++)
                {
                    //TextBox.Text += "Phone " + vc.Phones[i].PhoneTypes.ToString("G") + " " + vc.Phones[i].HomeWorkTypes.ToString() + (vc.Phones[i].Pref ? "Preferred" : "") + "=" + vc.Phones[i].Number + Environment.NewLine;

                    if (vc.Phones[i].HomeWorkTypes.ToString() == "None")
                    {
                        TextBox.Text += "Mobile Phone: ";
                        saveContactTask.MobilePhone = vc.Phones[i].Number;
                        TextBox.Text += saveContactTask.MobilePhone;
                        TextBox.Text += Environment.NewLine;
                    }
                    if (vc.Phones[i].HomeWorkTypes.ToString() == "WORK")
                    {
                        TextBox.Text += "Work Phone: ";
                        saveContactTask.WorkPhone = vc.Phones[i].Number;
                        TextBox.Text += saveContactTask.WorkPhone;
                        TextBox.Text += Environment.NewLine;
                    }
                    if (vc.Phones[i].HomeWorkTypes.ToString() == "HOME")
                    {
                        TextBox.Text += "Home Phone: ";
                        saveContactTask.HomePhone = vc.Phones[i].Number;
                        TextBox.Text += saveContactTask.HomePhone;
                        TextBox.Text += Environment.NewLine;
                    }
                }

                for (int i = 0; i < vc.Emails.Count; i++)
                {
                    //TextBox.Text += "Email " + " " + (vc.Emails[i].Pref ? "Preferred" : "") + "=" + vc.Emails[i].Address + Environment.NewLine;
                    saveContactTask.PersonalEmail = vc.Emails[i].Address;
                    TextBox.Text += saveContactTask.PersonalEmail;
                    TextBox.Text += Environment.NewLine;
                }

                /*
                if (!String.IsNullOrEmpty(vc.Prefix))
                    TextBox.Text += "Prefix=" + vc.Prefix + Environment.NewLine;
                if (!String.IsNullOrEmpty(vc.Prefix))
                     TextBox.Text += "Suffix=" + vc.Suffix+ Environment.NewLine;

                if (vc.Birthday > DateTime.MinValue)
                     TextBox.Text += "Birthday=" + vc.Birthday.ToLongDateString() + Environment.NewLine;
                if (vc.Rev != null)
                     TextBox.Text += "Rev=" + vc.Rev.ToLongDateString()+" "+vc.Rev.ToLongTimeString() + Environment.NewLine;

                if (!String.IsNullOrEmpty(vc.Org))
                     TextBox.Text += "Org=" + vc.Org + Environment.NewLine;            

                for (int i = 0; i < vc.URLs.Count; i++)
                {
                     TextBox.Text += "URL " + vc.URLs[i].HomeWorkTypes.ToString() + " " + "=" + vc.URLs[i].Address + Environment.NewLine;
                }

                for (int i = 0; i < vc.Addresses.Count; i++)
                {
                     TextBox.Text += "Address " + vc.Addresses[i].HomeWorkType.ToString() + "=" + vc.Addresses[i].POBox + ","
                    + vc.Addresses[i].Ext + ", "
                    + vc.Addresses[i].Street + ", "
                    + vc.Addresses[i].Locality + ", "
                    + vc.Addresses[i].Region + ", "
                    + vc.Addresses[i].Postcode + ", "
                    + vc.Addresses[i].Country + Environment.NewLine;
                }

                if (!String.IsNullOrEmpty(vc.Note))
                     TextBox.Text += "Note="+vc.Note;


                 TextBox.Text += "=============================" + Environment.NewLine;
                 */
                }
            }

        void saveContactTask_Completed(object sender, SaveContactResult e)
        {
            switch (e.TaskResult)
            {
                //Logic for when the contact was saved successfully
                case TaskResult.OK:
                    MessageBox.Show("Contact saved.");
                    break;

                //Logic for when the task was cancelled by the user
                case TaskResult.Cancel:
                    MessageBox.Show("Save cancelled.");
                    break;

                //Logic for when the contact could not be saved
                case TaskResult.None:
                    MessageBox.Show("Contact could not be saved.");
                    break;
            }
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            try
            {
                saveContactTask.Show();
            }
            catch (System.InvalidOperationException ex)
            {
                MessageBox.Show("An error occurred.");
            }
        }


    }


}

hope this helps



回答2:

OK, I found this:

We'll use the WindowsRuntimeStreamExtensions class to convert the System.IO Streams to the Windows.Storage.Streams objects:

System.IO.MemoryStream ms = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(VCardDataString));
IInputStream input= System.IO.WindowsRuntimeStreamExtensions.AsInputStream(ms);
                    ContactInformation info = await ContactInformation.ParseVcardAsync(input);
                    StoredContact phoneContact = new StoredContact(store, info);