protected override string ToJson(object value)
        {
            Guard.ArgumentNotNull(value, nameof(value));

            return(UmbrellaStatics.SerializeJson(value));
        }
        public void ContingentMerge(IOwinContext owinContext, string currentSiteName, bool reset, Func <CookieOptions> cookieOptionsBuilder)
        {
            owinContext.Request.CallCancelled.ThrowIfCancellationRequested();
            Guard.ArgumentNotNull(owinContext, nameof(owinContext));
            Guard.ArgumentNotNullOrWhiteSpace(currentSiteName, nameof(currentSiteName));
            Guard.ArgumentNotNull(cookieOptionsBuilder, nameof(cookieOptionsBuilder));

            try
            {
                string        currentCookieValue      = null;
                List <string> lstCurrentlyMergedSites = null;

                if (!reset)
                {
                    currentCookieValue = owinContext.Request.Cookies[_options.CrossSiteTrackingCookieName];

                    if (!string.IsNullOrWhiteSpace(currentCookieValue))
                    {
                        try
                        {
                            lstCurrentlyMergedSites = UmbrellaStatics.DeserializeJson <List <string> >(currentCookieValue);
                        }
                        catch (Exception exc) when(_log.WriteWarning(exc, new { currentCookieValue }, $"The cookie value of the {_options.CrossSiteTrackingCookieName} could not be deserialized to a List<string> instance.", returnValue: true))
                        {
                            // If the cookie couldn't be deserialized it has probably been tampered with.
                        }
                    }
                }

                if (lstCurrentlyMergedSites == null)
                {
                    lstCurrentlyMergedSites = new List <string>();
                }

                // At this point we should have either a new list to work with if we are resetting the cookie value
                // or an existing list from the cookie when not resetting. Either way, the next steps are the same.
                string currentSiteNameCleaned = currentSiteName.TrimToLowerInvariant();

                // Even though the cookie may have already been processed, we need to force processing in the event that there isn't a CurrentContact cookie
                // because it has been deleted manually or its value is invalid.
                if (!lstCurrentlyMergedSites.Contains(currentSiteNameCleaned) || !CurrentContactExists(owinContext))
                {
                    // We haven't merged the contact for this site yet.
                    Merge(owinContext.Request.User.Identity.Name);

                    lstCurrentlyMergedSites.Add(currentSiteNameCleaned);

                    // Update the cookie value on the outgoing response so that on the next request the merge doesn't happen again.
                    // Cleanup the values in case they have been tampered with or mangled somehow.
                    lstCurrentlyMergedSites = lstCurrentlyMergedSites.Select(x => x.TrimToLowerInvariant()).Distinct().ToList();

                    string updatedCookieValue = UmbrellaStatics.SerializeJson(lstCurrentlyMergedSites);

                    // We only need to update the cookie if its value has actually changed
                    if (!string.Equals(currentCookieValue, updatedCookieValue, StringComparison.OrdinalIgnoreCase))
                    {
                        owinContext.Response.Cookies.Append(_options.CrossSiteTrackingCookieName, updatedCookieValue, cookieOptionsBuilder());
                    }
                }
            }
            catch (Exception exc) when(_log.WriteError(exc, new { owinContext.Request.User.Identity.Name, reset }, returnValue: true))
            {
                throw new KenticoContactException("There has been a problem merging the contact data for the specified user for the specified OwinContext.", exc);
            }
        }