参照jQuery.ajax改造tangram的baidu.url.jsonToQuery()方法使之支持json嵌套对象的解析

背景:项目里用的tangram1.5.2基础库,原因就不说了。是个整站式Ajax应用,Ajax数据的传输在项目中处于至关重要的地位。

我们前后端交互的数据中包括多层json嵌套的场景,比如:

var data = {
    list1 : [ 0, 1, 2, 3],
    list2 : [
        { key : 'hello', value : 'world' },
        { key : 'img', value : 'src' }
    ]
};

由于Ajax支持的传输方式只能是String,因而必须在请求发出前将json解析成string。tangram中提供的json转义方法可用的就只有baidu.url.jsonToQuery()了。其源码如下:

/**
 * 将json对象解析成query字符串
 * @name baidu.url.jsonToQuery
 * @function
 * @grammar baidu.url.jsonToQuery(json[, replacer])
 * @param {Object} json 需要解析的json对象
 * @param {Function=} replacer_opt 对值进行特殊处理的函数,function (value, key)
 * @see baidu.url.queryToJson,baidu.url.getQueryValue
 *             
 * @return {string} - 解析结果字符串,其中值将被URI编码,{a:'&1 '} ==> "a=%261%20"。
 */
baidu.url.jsonToQuery = function (json, replacer_opt) {
    var result = [], 
        itemLen,
        replacer = replacer_opt || function (value) {
          return baidu.url.escapeSymbol(value);
        };
        
    baidu.object.each(json, function(item, key){
        // 这里只考虑item为数组、字符串、数字类型,不考虑嵌套的object
        if (baidu.lang.isArray(item)) {
            itemLen = item.length;
            // value的值需要encodeURIComponent转义吗?
            // FIXED 优化了escapeSymbol函数
            while (itemLen--) {
                result.push(key + '=' + replacer(item[itemLen], key));
            }
        } else {
            result.push(key + '=' + replacer(item, key));
        }
    });
    
    return result.join('&');
};

用这方法baidu.url.jsonToQuery( data )的结果是: list1=3&list1=2&list1=1&list1=0&list2=[object%20Object]&list2=[object%20Object]

完全不是期待中的结果。

通过查看源码发现,这货不支持多层嵌套的Object。没办法,只能自己动手将它改装了。

jQuery Ajax中序列化ajax传输数据的源码:

jQuery.extend({
    /* ..... other methods  */

    // Serialize an array of form elements or a set of
    // key/values into a query string
    param: function( a, traditional ) {
        var s = [],
            add = function( key, value ) {
                // If value is a function, invoke it and return its value
                value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
                s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
            };

        // Set traditional to true for jQuery <= 1.3.2 behavior.
        if ( traditional === undefined ) {
            traditional = jQuery.ajaxSettings.traditional;
        }

        // If an array was passed in, assume that it is an array of form elements.
        if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
            // Serialize the form elements
            jQuery.each( a, function() {
                add( this.name, this.value );
            });

        } else {
            // If traditional, encode the "old" way (the way 1.3.2 or older
            // did it), otherwise encode params recursively.
            for ( var prefix in a ) {
                buildParams( prefix, a[ prefix ], traditional, add );
            }
        }

        // Return the resulting serialization
        return s.join( "&" ).replace( r20, "+" );
    }
});

function buildParams( prefix, obj, traditional, add ) {
    if ( jQuery.isArray( obj ) ) {
        // Serialize array item.
        jQuery.each( obj, function( i, v ) {
            if ( traditional || rbracket.test( prefix ) ) {
                // Treat each array item as a scalar.
                add( prefix, v );

            } else {
                // If array item is non-scalar (array or object), encode its
                // numeric index to resolve deserialization ambiguity issues.
                // Note that rack (as of 1.0.0) can't currently deserialize
                // nested arrays properly, and attempting to do so may cause
                // a server error. Possible fixes are to modify rack's
                // deserialization algorithm or to provide an option or flag
                // to force array serialization to be shallow.
                buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
            }
        });

    } else if ( !traditional && jQuery.type( obj ) === "object" ) {
        // Serialize object item.
        for ( var name in obj ) {
            buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
        }

    } else {
        // Serialize scalar item.
        add( prefix, obj );
    }
}

不得不说JQ的数据容错、完备性做的真心好。

改装后:

/**
 * 将json对象解析成query字符串
 * @name baidu.url.jsonToQuery
 * @function
 * @grammar baidu.url.jsonToQuery(json[, replacer])
 * @param {Object} json 需要解析的json对象
 * @param {Function=} replacer_opt 对值进行特殊处理的函数,function (value, key)
 * @see baidu.url.queryToJson,baidu.url.getQueryValue
 *
 * @modify 加入嵌套的json对象的解析方法(参照JQ实现)
 *             
 * @return {string} - 解析结果字符串,其中值将被URI编码,{a:'&1 '} ==> "a=%261%20"。
 */
baidu.url.jsonToQuery = function (json, replacer_opt) {
    var result = [], 
        itemLen,
        replacer = replacer_opt || function (value) {
          return baidu.url.escapeSymbol(value);
        },
        add = function( key, value ) {
            // If value is a function, invoke it and return its value
            value = baidu.lang.isFunction( value ) ? value() : ( value == null ? "" : value );
            result[ result.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
        };

    function buildParams( prefix, obj, add ) {
        // 考虑嵌套的object
        if ( baidu.lang.isArray(obj) ) {
            itemLen = obj.length;
            // value的值需要encodeURIComponent转义吗?
            // FIXED 优化了escapeSymbol函数
            baidu.array.each( obj, function( item, i ) {
                buildParams( prefix + "[" + ( typeof item === "object" ? i : "" ) + "]", item, add );
            } );
                
        } else if ( typeof obj === "object" ) {
            for ( var name in obj ) {
                buildParams( prefix + "[" + name + "]", obj[ name ], add );
            }
        } else {
            // Serialize scalar item.
            add( prefix, obj );
        }
    }
        
    baidu.object.each(json, function(item, key){
        // 增加嵌套的object的计算
        buildParams( key, item, add );
    });
    
    return s.join('&');
};

再次用baidu.url.jsonToQuery(data)试试:

list1%5B%5D=0&list1%5B%5D=1&list1%5B%5D=2&list1%5B%5D=3&list2%5B0%5D%5Bkey%5D=hello&list2%5B0%5D%5Bvalue%5D=world&list2%5B1%5D%5Bkey%5D=img&list2%5B1%5D%5Bvalue%5D=src

嗯,这才是我们想要的。搞定~ ^ ^

posted @ 2012-07-04 12:21  virola  阅读(769)  评论(0)    收藏  举报