private static ReadOnlyCollection <VocabularyKey> CreateVocabularyKeysFromResponse(
            string methodNameNSSuffix,
            HealthServiceResponseData response)
        {
            XPathNavigator infoNav =
                response.InfoNavigator.SelectSingleNode(
                    Vocabulary.GetInfoXPathExpression(
                        methodNameNSSuffix,
                        response.InfoNavigator));

            List <VocabularyKey> vocabularyKeys = new List <VocabularyKey>();

            XPathNodeIterator vocabKeyIter = infoNav.Select("vocabulary-key");

            foreach (XPathNavigator vocabKeyNav in vocabKeyIter)
            {
                VocabularyKey vocabularyKey = new VocabularyKey();
                vocabularyKey.ParseXml(vocabKeyNav);
                vocabularyKeys.Add(vocabularyKey);
            }

            return(new ReadOnlyCollection <VocabularyKey>(vocabularyKeys));
        }
        /// <summary>
        /// Searches a specific vocabulary and retrieves the matching vocabulary items.
        /// </summary>
        ///
        /// <remarks>
        /// This method does text search matching of display text and abbreviation text
        /// for the culture defined by the <see cref="HealthServiceConnection.Culture"/>.
        /// The <paramref name="searchValue"/> is a string of characters in the specified
        /// culture.
        /// </remarks>
        ///
        /// <param name="connection">
        /// The connection to use for this operation. The connection
        /// must have application capability.
        /// </param>
        ///
        /// <param name="vocabularyKey">
        /// The <see cref="VocabularyKey"/> defining the vocabulary to search. If the
        /// family is not specified, the default HealthVault vocabulary family is used.
        /// If the version is not specified, the most current version of the vocabulary
        /// is used.
        /// </param>
        ///
        /// <param name="searchValue">
        /// The search string to use.
        /// </param>
        ///
        /// <param name="searchType">
        /// The type of search to perform.
        /// </param>
        ///
        /// <param name="maxResults">
        /// The maximum number of results to return. If null, all matching results
        /// are returned, up to a maximum number defined by the service config
        /// value with key maxResultsPerVocabularyRetrieval.
        /// </param>
        ///
        /// <exception cref="ArgumentException">
        /// If <paramref name="vocabularyKey"/> is <b>null</b>.
        /// <br></br>
        /// -Or-
        /// <br></br>
        /// If <paramref name="searchValue"/> is <b>null</b> or empty or greater
        /// than <b>255</b> characters.
        /// <br></br>
        /// -Or-
        /// <br></br>
        /// if <paramref name="searchType"/> is not a known
        /// <see cref="VocabularySearchType"/> value.
        /// <br></br>
        /// -Or-
        /// <br></br>
        /// when <paramref name="maxResults"/> is defined but has a value less than 1.
        /// </exception>
        ///
        /// <exception cref="HealthServiceException">
        /// There is an error in the server request.
        /// <br></br>
        /// -Or-
        /// <br></br>
        /// The requested vocabulary is not found on the server.
        /// <br></br>
        /// -Or-
        /// The requested search culture is not supported.
        /// </exception>
        ///
        public virtual async Task <VocabularySearchResult> SearchVocabularyAsync(
            IHealthVaultConnection connection,
            VocabularyKey vocabularyKey,
            string searchValue,
            VocabularySearchType searchType,
            int?maxResults)
        {
            if (string.IsNullOrEmpty(searchValue) || searchValue.Length > 255)
            {
                throw new ArgumentException(Resources.VocabularySearchStringInvalid, nameof(searchValue));
            }

            if (!Enum.IsDefined(typeof(VocabularySearchType), searchType))
            {
                throw new ArgumentException(Resources.VocabularySearchTypeUnknown, nameof(searchType));
            }

            if (maxResults.HasValue && maxResults.Value < 1)
            {
                throw new ArgumentException(Resources.SearchMaxResultsInvalid, nameof(maxResults));
            }

            var method        = HealthVaultMethods.SearchVocabulary;
            int methodVersion = 1;

            StringBuilder     requestParameters = new StringBuilder(256);
            XmlWriterSettings settings          = SDKHelper.XmlUnicodeWriterSettings;

            settings.OmitXmlDeclaration = true;
            settings.ConformanceLevel   = ConformanceLevel.Fragment;

            using (XmlWriter writer = XmlWriter.Create(requestParameters, settings))
            {
                vocabularyKey?.WriteXml(writer);

                writer.WriteStartElement("text-search-parameters");

                writer.WriteStartElement("search-string");
                writer.WriteAttributeString("search-mode", searchType.ToString());
                writer.WriteString(searchValue);
                writer.WriteEndElement(); // <search-string>

                if (maxResults.HasValue)
                {
                    writer.WriteElementString("max-results", maxResults.Value.ToString(CultureInfo.InvariantCulture));
                }

                writer.WriteEndElement();
                writer.Flush();
            }

            string parameters = requestParameters.ToString();

            HealthServiceResponseData responseData = await connection.ExecuteAsync(method, methodVersion, parameters).ConfigureAwait(false);

            if (vocabularyKey != null)
            {
                return(new VocabularySearchResult(CreateVocabularyItemCollectionFromResponse(method.ToString(), responseData)));
            }

            return(new VocabularySearchResult(CreateVocabularyKeysFromResponse(method.ToString(), responseData)));
        }