I’m really surprised that the jQuery auto complete plug-in does not support XML by default. Almost all web services have the option to transmit XML, that it should be a requirement that every Ajax plug-in or widget consumes XML. If your using ASP.NET 2.0 and you want to use the jQuery auto-complete plug-in, you’ll notice this isn’t an easy task. Since the default plug-in requires a flat file with a list of values separated by new row “\r”. To support this natively, you can use a ASHX handler, but I wanted to use my existing web services. To get around the problem, I created a “parseXML” function and added an if statement in the $ajax results function.

The change really came down to adding an if statement for dataType = “xml” and added a new property datakey to support the parsing of the xml file. This way, I can parse ASMX 2.0 Web Service calls or static XML files.

[UPDATE] There is a catch to “STATIC” xml data, the default plug-in is based on the assumption that your results (aka: XML data) will only contain filtered results. If your data contains all possible results, you’ll need to filter the data… See comments for instructions! The XML parsing is done using jQuery built-in XML parser, so you don’t have to worry about dealing with browser specific parsers.

Here is the modified request function and the new parseXML function that I added to the “jQuery.autocomplete.js” plug-in:

        function request(term, success, failure) {
            if (!options.matchCase)
                term = term.toLowerCase();
            var data = cache.load(term);
            // recieve the cached data
            if (data && data.length) {
                success(term, data);
                // if an AJAX url has been supplied, try loading the data now
            } else if ((typeof options.url == "string") && (options.url.length > 0)) {

                var extraParams = {
                    timestamp: +new Date()
                };
                $.each(options.extraParams, function(key, param) {
                    extraParams[key] = typeof param == "function" ? param() : param;
                });

                $.ajax({
                    // try to leverage ajaxQueue plugin to abort previous requests
                    mode: "abort",
                    // limit abortion to this input
                    // port: "autocomplete" + input.name,
                    dataType: options.dataType,
                    url: options.url,
                    data: $.extend({
                        q: lastWord(term),
                        limit: options.max
                    }, extraParams),
                    success: function(data) {
                        var parsed = [];

                        // Added Logic by Zach
                        // If dataType = "XML" use the parseXML fuction
                        if (options.dataType == "xml") {
                            parsed = parseXML(data);
                        }
                        else {
                            parsed = options.parse && options.parse(data) || parse(data);
                        }
                        cache.add(term, parsed);
                        success(term, parsed);
                    }
                });
            } else {
                // if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
                select.emptyList();
                failure(term);
            }
        };

        // Added Logic by Zach
        // Added XML Parse Function
        function parseXML(xml) {
            var parsed = [];

            $(xml).find(options.datakey).each(function() {
                parsed[parsed.length] = {
                    data: [$(this).text()],
                    value: $(this).text(),
                    result: options.formatResult && options.formatResult($(this).text(), [$(this).text()]) || [$(this).text()]
                };
            });
            return parsed;
        }

In order to use this new functionality, you’ll need to add the following options when you bind to a XML data source.

    $(document).ready(function() {
        $("#customerCode").autocomplete("Customers.xml", { dataType: "xml", datakey: "customer" });
    });

Here is the sample XML file used in the auto complete instance above.

<?xml version="1.0" encoding="utf-8" ?>
<CUSTOMERS>
    <CUSTOMER>Albert</CUSTOMER>
    <CUSTOMER>Allen</CUSTOMER>
    <CUSTOMER>Brandy</CUSTOMER>
    <CUSTOMER>Brink</CUSTOMER>
    <CUSTOMER>Cathy</CUSTOMER>
    <CUSTOMER>Candy</CUSTOMER>
    <CUSTOMER>Crystal</CUSTOMER>
    <CUSTOMER>Deon</CUSTOMER>
    <CUSTOMER>Dirk</CUSTOMER>
    <CUSTOMER>Erie</CUSTOMER>
    <CUSTOMER>Eve</CUSTOMER>
    <CUSTOMER>Francis</CUSTOMER>
    <CUSTOMER>Gina</CUSTOMER>
    <CUSTOMER>Harry</CUSTOMER>
    <CUSTOMER>Ingrid</CUSTOMER>
    <CUSTOMER>Janice</CUSTOMER>
    <CUSTOMER>Kelly</CUSTOMER>
    <CUSTOMER>Kylee</CUSTOMER>
    <CUSTOMER>Nathan</CUSTOMER>
    <CUSTOMER>Zachary</CUSTOMER>
</CUSTOMERS>

The only requirement for your ASMX web service, is your method signature must accept a string parameter called “q”. You can add additional parameters on your method, by default the auto-complete sends (q,limit,timestamp) but only “q” is required.

Here is a link to the official jQuery autocomplete plug-in.

Here is a complete ASP.NET 2.0 Web Application sample with the modified auto complete plug-in jQueryAutocompleteASMX.zip

[EDIT 5/14] by Zach :

        $(document).ready(function() {
            $("#textboxCustomer").autocomplete("/Autocomplete.asmx/GetCustomers", {
                parse: function(data) {
                    var parsed = [];

                    $(data).find("string").each(function() {
                        parsed[parsed.length] = {
                            data: [$(this).text()],
                            value: $(this).text(),
                            result: [$(this).text()]
                        };
                    });
                    return parsed;
                },
                dataType: "xml", datakey: "string", max: 5
            });
        });

Using the function above, you can replace the built-in auto-complete parse function with a dynamic parser. The only requirement is returning string array, which is the same object returned by the default function. I think the original example is useful if you are using the jQuery plug-in with ASP.NET and .NET Web Services, but if I was only going to use the plug-in a few times… I’d use this solution. -Zach

[EDIT 5/19] by Zach:
Don’t forget the following lines should be added to your web.config under “<system.web>” to support GET & POST requests for your web services.

        <!-- Added WebServices Section -->
        <webServices>
            <protocols>
                <add name="HttpGet"/>
                <add name="HttpPost"/>
            </protocols>
        </webServices>