Name Validation 2 C# Code Snippet

using NV2Reference;

namespace name_validation_2_dot_net.SOAP
{
    /// <summary>
    /// Provides functionality to call the ServiceObjects DOTS Name Validation 2 SOAP service's ValidateNameV2 operation,
    /// retrieving name validation information (e.g., name parsing, gender, scores) with fallback to a backup endpoint
    /// for reliability in live mode.
    /// </summary>
    public class NameInfoV2Validation
    {
        private const string LiveBaseUrl = "https://sws.serviceobjects.com/NV2/api.svc/soap";
        private const string BackupBaseUrl = "https://swsbackup.serviceobjects.com/NV2/api.svc/soap";
        private const string TrialBaseUrl = "https://trial.serviceobjects.com/NV2/api.svc/soap";

        private readonly string _primaryUrl;
        private readonly string _backupUrl;
        private readonly int _timeoutMs;
        private readonly bool _isLive;

        /// <summary>
        /// Initializes URLs/timeout/IsLive.
        /// </summary>
        public NameInfoV2Validation(bool isLive)
        {
            _timeoutMs = 10000; 
            _isLive = isLive;

            _primaryUrl = isLive ? LiveBaseUrl : TrialBaseUrl;
            _backupUrl = isLive ? BackupBaseUrl : TrialBaseUrl;

            if (string.IsNullOrWhiteSpace(_primaryUrl))
                throw new InvalidOperationException("Primary URL not set.");
            if (string.IsNullOrWhiteSpace(_backupUrl))
                throw new InvalidOperationException("Backup URL not set.");
        }

        /// <summary>
        /// This is the primary operation for validating and parsing a name. Given a name and optional parameters,
        /// </summary>
        /// <param name="Name">The name to validate (required).</param>
        /// <param name="Option">Comma-separated list of options for additional processing (optional).</param>
        /// <param name="LicenseKey">Your license key to use the service.</param>
        /// <returns>A <see cref="Task{NameInfoV2Response}"/> containing an <see cref="NameInfoV2Response"/> object with name validation details or an error.</returns>
        /// <exception cref="Exception">Thrown if both primary and backup endpoints fail.</exception>
        public async Task<NameInfoV2Response> ValidateNameV2(string Name, string Option, string LicenseKey)
        {
            NV2LibraryClient clientPrimary = null;
            NV2LibraryClient clientBackup = null;

            try
            {
                // Attempt primary endpoint
                clientPrimary = new NV2LibraryClient();
                clientPrimary.Endpoint.Address = new System.ServiceModel.EndpointAddress(_primaryUrl);
                clientPrimary.InnerChannel.OperationTimeout = TimeSpan.FromMilliseconds(_timeoutMs);

                NameInfoV2Response response = await clientPrimary.ValidateNameV2Async(
                    Name, Option, LicenseKey).ConfigureAwait(false);

                if (_isLive && !IsValid(response))
                {
                    throw new InvalidOperationException("Primary endpoint returned null or a fatal TypeCode=3 error for ValidateNameV2");
                }
                return response;
            }
            catch (Exception primaryEx)
            {
                try
                {
                    clientBackup = new NV2LibraryClient();
                    clientBackup.Endpoint.Address = new System.ServiceModel.EndpointAddress(_backupUrl);
                    clientBackup.InnerChannel.OperationTimeout = TimeSpan.FromMilliseconds(_timeoutMs);

                    return await clientBackup.ValidateNameV2Async(
                        Name, Option, LicenseKey).ConfigureAwait(false);
                }
                catch (Exception backupEx)
                {
                    throw new Exception(
                        $"Both primary and backup endpoints failed.\n" +
                        $"Primary error: {primaryEx.Message}\n" +
                        $"Backup error: {backupEx.Message}");
                }
                finally
                {
                    clientBackup?.Close();
                }
            }
            finally
            {
                clientPrimary?.Close();
            }
        }

        private static bool IsValid(NameInfoV2Response response) =>
            response?.Error == null || response.Error.TypeCode != "3";
    }
}

Name Validation 2 Python Code Snippet

from suds.client import Client
from suds import WebFault
from suds.sudsobject import Object

class NameInfoV2Soap:

    def __init__(self, license_key: str, is_live: bool, timeout_ms: int = 10000):
        """
        license_key: Service Objects NV2 license key.
        is_live: Whether to use live or trial endpoints
        timeout_ms: SOAP call timeout in milliseconds
        """

        self._timeout_s = timeout_ms / 1000.0
        self._is_live = is_live
        self.license_key = license_key

        # WSDL URLs for primary and backup endpoints
        self._primary_wsdl = (
            "https://sws.serviceobjects.com/NV2/api.svc?wsdl"
            if is_live else
            "https://trial.serviceobjects.com/NV2/api.svc?wsdl"
        )
        self._backup_wsdl = (
            "https://swsbackup.serviceobjects.com/NV2/api.svc?wsdl"
            if is_live else
            "https://trial.serviceobjects.com/NV2/api.svc?wsdl"
        )

    def get_name_info(self, name: str, option: str) -> Object:
        """
        Calls the Name Validation 2 ValidateNameV2 SOAP API to retrieve name validation information.

        Parameters:
        name (str): The name to validate.
        option (str): Comma-separated list of options for additional processing (optional).
        license_key: Service Objects Name Validation license key.
        is_live: Whether to use live or trial endpoints
        timeout_ms: SOAP call timeout in milliseconds

        Returns:
            Object: Raw SOAP response with name information or error details.
        """

        # Common kwargs for both calls
        call_kwargs = dict(
            Name=name,
            Option=option,
            LicenseKey=self.license_key
        )

        # Attempt primary
        try:
            client = Client(self._primary_wsdl, timeout=self._timeout_s)
            # Override endpoint URL if needed:
            # client.set_options(location=self._primary_wsdl.replace('?wsdl', '/soap'))
            response = client.service.ValidateNameV2(**call_kwargs)

            # If response is None or fatal error code, trigger fallback
            if response is None or (hasattr(response, 'Error') and response.Error and response.Error.TypeCode == '3'):
                raise ValueError("Primary returned no result or fatal Error.TypeCode=3")

            return response

        except (WebFault, ValueError, Exception) as primary_ex:
            try:
                client = Client(self._backup_wsdl, timeout=self._timeout_s)
                response = client.service.ValidateNameV2(**call_kwargs)

                if response is None:
                    raise ValueError("Backup returned no result")

                return response

            except (WebFault, Exception) as backup_ex:
                # Raise a combined error if both attempts fail
                msg = (
                    "Both primary and backup endpoints failed.\n"
                    f"Primary error: {str(primary_ex)}\n"
                    f"Backup error: {str(backup_ex)}"
                )
                raise RuntimeError(msg)

Name Validation 2 NodeJS Code Snippet

import { soap } from 'strong-soap';
import { NameInfoV2Response } from './nv2_response.js';

/**
 * <summary>
 * A class that provides functionality to call the ServiceObjects Name Validation 2 SOAP service's NameInfoV2 endpoint,
 * retrieving name validation information with fallback to a backup endpoint for reliability in live mode.
 * </summary>
 */
class NameInfoV2Soap {
    /**
     * <summary>
     * Initializes a new instance of the NameInfoV2Soap class with the provided input parameters,
     * setting up primary and backup WSDL URLs based on the live/trial mode.
     * </summary>
     * @param {string} Name - The name to validate.
     * @param {string} Option - Comma-separated list of options for additional processing (optional).
     * @param {string} LicenseKey - Your license key to use the service.
     * @param {boolean} isLive - Value to determine whether to use the live or trial servers.
     * @param {number} timeoutSeconds - Timeout, in seconds, for the call to the service.
     * @throws {Error} Thrown if LicenseKey is empty or null.
     */
    constructor(Name, Option = '', LicenseKey, isLive = true, timeoutSeconds = 15) {
        if (!LicenseKey) {
            throw new Error("LicenseKey is required and cannot be empty or null.");
        }

        this.args = {
            Name,
            Option,
            LicenseKey
        };

        this.isLive = isLive;
        this.timeoutSeconds = timeoutSeconds;

        this.LiveBaseUrl = "https://sws.serviceobjects.com/NV2/api.svc?wsdl";
        this.BackupBaseUrl = "https://swsbackup.serviceobjects.com/NV2/api.svc?wsdl";
        this.TrialBaseUrl = "https://trial.serviceobjects.com/NV2/api.svc?wsdl";

        this._primaryWsdl = this.isLive ? this.LiveBaseUrl : this.TrialBaseUrl;
        this._backupWsdl = this.isLive ? this.BackupBaseUrl : this.TrialBaseUrl;
    }

    /**
     * <summary>
     * Asynchronously calls the NameInfoV2 SOAP endpoint, attempting the primary endpoint
     * first and falling back to the backup if the response is invalid (Error.TypeCode == '3') in live mode
     * or if the primary call fails.
     * </summary>
     * <returns type="Promise<NameInfoV2Response>">A promise that resolves to a NameInfoV2Response object containing name validation details or an error.</returns>
     * <exception cref="Error">Thrown if both primary and backup calls fail, with detailed error messages.</exception>
     */
    async getNameInfo() {
        try {
            const primaryResult = await this._callSoap(this._primaryWsdl, this.args);

            if (this.isLive && !this._isValid(primaryResult)) {
                console.warn("Primary returned Error.TypeCode == '3', falling back to backup...");
                const backupResult = await this._callSoap(this._backupWsdl, this.args);
                return backupResult;
            }

            return primaryResult;
        } catch (primaryErr) {
            try {
                const backupResult = await this._callSoap(this._backupWsdl, this.args);
                return backupResult;
            } catch (backupErr) {
                throw new Error(`Both primary and backup calls failed:\nPrimary: ${primaryErr.message}\nBackup: ${backupErr.message}`);
            }
        }
    }

    /**
     * <summary>
     * Performs a SOAP service call to the specified WSDL URL with the given arguments,
     * creating a client and processing the response into a NameInfoV2Response object.
     * </summary>
     * <param name="wsdlUrl" type="string">The WSDL URL of the SOAP service endpoint (primary or backup).</param>
     * <param name="args" type="Object">The arguments to pass to the NameInfoV2 method.</param>
     * <returns type="Promise<NameInfoV2Response>">A promise that resolves to a NameInfoV2Response object containing the SOAP response data.</returns>
     * <exception cref="Error">Thrown if the SOAP client creation fails, the service call fails, or the response cannot be parsed.</exception>
     */
    _callSoap(wsdlUrl, args) {
        return new Promise((resolve, reject) => {
            soap.createClient(wsdlUrl, { timeout: this.timeoutSeconds * 1000 }, (err, client) => {
                if (err) return reject(err);

                client.ValidateNameV2(args, (err, result) => {
                    const rawData = result?.ValidateNameV2Result;
                    try {
                        if (!rawData) {
                            return reject(new Error("SOAP response is empty or undefined."));
                        }
                        const parsed = new NameInfoV2Response(rawData);
                        resolve(parsed);
                    } catch (parseErr) {
                        reject(new Error(`Failed to parse SOAP response: ${parseErr.message}`));
                    }
                });
            });
        });
    }

    /**
     * <summary>
     * Checks if a SOAP response is valid by verifying that it exists and either has no Error object
     * or the Error.TypeCode is not equal to '3'.
     * </summary>
     * <param name="response" type="NameInfoV2Response">The NameInfoV2Response object to validate.</param>
     * <returns type="boolean">True if the response is valid, false otherwise.</returns>
     */
    _isValid(response) {
        return response && (!response.Error || response.Error.TypeCode !== "3");
    }
}

export { NameInfoV2Soap };


class BestGuessName {
    constructor(data = {}) {
        this.Prefix = data.Prefix || null;
        this.FirstName = data.FirstName || null;
        this.MiddleName = data.MiddleName || null;
        this.LastName = data.LastName || null;
        this.Suffix = data.Suffix || null;
    }

    toString() {
        return `{Prefix: ${this.Prefix}\n` +
               `FirstName: ${this.FirstName}\n` +
               `MiddleName: ${this.MiddleName}\n` +
               `LastName: ${this.LastName}\n` +
               `Suffix: ${this.Suffix}}`;
    }
}

class NameInfoV2 {
    constructor(data = {}) {
        this.BestGuessName = data.BestGuessName ? new BestGuessName(data.BestGuessName) : null;
        this.NameIn = data.NameIn || null;
        this.NameClassification = data.NameClassification || null;
        this.Prefix = data.Prefix || null;
        this.FirstName = data.FirstName || null;
        this.MiddleName = data.MiddleName || null;
        this.LastName = data.LastName || null;
        this.Suffix = data.Suffix || null;
        this.FirstNameFound = data.FirstNameFound || null;
        this.IsCommonFirstName = data.IsCommonFirstName || null;
        this.FirstNameOrigin = data.FirstNameOrigin || null;
        this.FirstNameSimilar = data.FirstNameSimilar || null;
        this.LastNameFound = data.LastNameFound || null;
        this.IsCommonLastName = data.IsCommonLastName || null;
        this.LastNameOrigin = data.LastNameOrigin || null;
        this.LastNameSimilar = data.LastNameSimilar || null;
        this.Gender = data.Gender || null;
        this.FirstNameAlt = data.FirstNameAlt || null;
        this.MiddleNameAlt = data.MiddleNameAlt || null;
        this.LastNameAlt = data.LastNameAlt || null;
        this.FirstNameAltFound = data.FirstNameAltFound || null;
        this.LastNameAltFound = data.LastNameAltFound || null;
        this.GenderAlt = data.GenderAlt || null;
        this.RelatedNames = data.RelatedNames || null;
        this.IsCorrectedName = data.IsCorrectedName || null;
        this.IsBusinessName = data.IsBusinessName || null;
        this.BusinessName = data.BusinessName || null;
        this.VulgarityScore = data.VulgarityScore || null;
        this.CelebrityScore = data.CelebrityScore || null;
        this.BogusScore = data.BogusScore || null;
        this.GarbageScore = data.GarbageScore || null;
        this.FirstNameDictionaryScore = data.FirstNameDictionaryScore || null;
        this.MiddleNameDictionaryScore = data.MiddleNameDictionaryScore || null;
        this.LastNameDictionaryScore = data.LastNameDictionaryScore || null;
        this.OverallNameScore = data.OverallNameScore || null;
        this.IsNameGood = data.IsNameGood || null;
        this.StatusCodes = data.StatusCodes || null;
        this.Status = data.Status || null;
    }

    toString() {
        return `{BestGuessName: ${this.BestGuessName ? this.BestGuessName.toString() : 'null'}\n` +
               `NameIn: ${this.NameIn}\n` +
               `NameClassification: ${this.NameClassification}\n` +
               `Prefix: ${this.Prefix}\n` +
               `FirstName: ${this.FirstName}\n` +
               `MiddleName: ${this.MiddleName}\n` +
               `LastName: ${this.LastName}\n` +
               `Suffix: ${this.Suffix}\n` +
               `FirstNameFound: ${this.FirstNameFound}\n` +
               `IsCommonFirstName: ${this.IsCommonFirstName}\n` +
               `FirstNameOrigin: ${this.FirstNameOrigin}\n` +
               `FirstNameSimilar: ${this.FirstNameSimilar}\n` +
               `LastNameFound: ${this.LastNameFound}\n` +
               `IsCommonLastName: ${this.IsCommonLastName}\n` +
               `LastNameOrigin: ${this.LastNameOrigin}\n` +
               `LastNameSimilar: ${this.LastNameSimilar}\n` +
               `Gender: ${this.Gender}\n` +
               `FirstNameAlt: ${this.FirstNameAlt}\n` +
               `MiddleNameAlt: ${this.MiddleNameAlt}\n` +
               `LastNameAlt: ${this.LastNameAlt}\n` +
               `FirstNameAltFound: ${this.FirstNameAltFound}\n` +
               `LastNameAltFound: ${this.LastNameAltFound}\n` +
               `GenderAlt: ${this.GenderAlt}\n` +
               `RelatedNames: ${this.RelatedNames}\n` +
               `IsCorrectedName: ${this.IsCorrectedName}\n` +
               `IsBusinessName: ${this.IsBusinessName}\n` +
               `BusinessName: ${this.BusinessName}\n` +
               `VulgarityScore: ${this.VulgarityScore}\n` +
               `CelebrityScore: ${this.CelebrityScore}\n` +
               `BogusScore: ${this.BogusScore}\n` +
               `GarbageScore: ${this.GarbageScore}\n` +
               `FirstNameDictionaryScore: ${this.FirstNameDictionaryScore}\n` +
               `MiddleNameDictionaryScore: ${this.MiddleNameDictionaryScore}\n` +
               `LastNameDictionaryScore: ${this.LastNameDictionaryScore}\n` +
               `OverallNameScore: ${this.OverallNameScore}\n` +
               `IsNameGood: ${this.IsNameGood}\n` +
               `StatusCodes: ${this.StatusCodes}\n` +
               `Status: ${this.Status}\n`;
    }
}

class Error {
    constructor(data = {}) {
        this.Type = data.Type || null;
        this.TypeCode = data.TypeCode || null;
        this.Desc = data.Desc || null;
        this.DescCode = data.DescCode || null;
    }

    toString() {
        return `Type: ${this.Type}\n` +
               `TypeCode: ${this.TypeCode}\n` +
               `Desc: ${this.Desc}\n` +
               `DescCode: ${this.DescCode} `;
    }
}

class NameInfoV2Response {
    constructor(data = {}) {
        this.NameInfoV2 = data.NameInfoV2 ? new NameInfoV2(data.NameInfoV2) : null;
        this.Error = data.Error ? new Error(data.Error) : null;
    }

    toString() {
        return `NameInfoV2: ${(this.NameInfoV2 ? this.NameInfoV2.toString() : 'null')}\n` +
               `Error: ${this.Error ? this.Error.toString() : 'null'}`;
    }
}

export{ NameInfoV2Response };