📜 ⬆️ ⬇️

Hiqus - HIerarhical QUery String

This is the format for representing tree data structures in the form of a single line in a human-friendly form. It is a generalization of the format “application / x-www-form-urlencoded” and, as a result, is backward compatible with it. Hiqus is based on the same principle of presenting data in the form of key-value pairs with the only difference that a key can be composite or empty.

This format is already used by such monsters as Yandex (http://yandex.ru/yandsearch?date=within&text=hiqus&from_day=28&from_month=4&from_year=2009), Google (http://www.google.ru/search?as_q=hiqus&hl= ru & num = 10 & as_qdr = all) and many others who need to pass hierarchical data in the query string. An exception is PHP sites, for which traditionally their own, not too visual format is used (for example, I did not find it, but it looks like this:? User% 5Bid% 5D = 123 & user% 5Bname% 5D = Nick).

Description

A Hiqus line consists of 1 or more pieces, separated by a separator, which can be an ampersand that is familiar to us from ordinary query string, a slash from a traditional CNC, a space from a command line, a dot-and-comma from a cookie, Well, to the heap - a vertical line ;-)

Each "piece" consists of the actual value and the path before it. The path may be empty. After each element of the path, there should be a special separator, which can be an equal sign familiar to us from ordinary query string, from JSON - a colon, and convenient to use in HTML forms - an underscore (because it is not encoded during transmission).
')
The value and path elements must be encoded with a percentage in accordance with the URI specification. However, only the special characters mentioned should be important to the parser. Whether to screen the rest depends on where and how the hiqus string will be used.

The path element can be an empty string, then an automatically generated number will be used instead. For example, “a == 1” is equivalent to “a = 0 = 1” when parsing.

If there is only 1 element in the path, and that one is empty, then the separator placed after it can be lowered. For example, "= blogs / = webstandards" is equivalent to "blogs / webstandards".

“Pieces” are added to the target tree sequentially, which allows subsequent overwriting of the values ​​and even subtrees of the previous ones. For example, “a = b = 1 / a = 1” is equivalent to “a = 1”.

Examples

Suppose we have such a CNC: / blogs / webstandards / 92300 /
If you parse it as hiqus, you will get an object with the following JSON view: {'0': 'blogs', '1': 'webstandarts', '2': '92300'}

Let we sent the form and received such query string: q = hiqus & target_type = blogs & target_subtype = offtopic
The result of the parsing will be something like this: {q: 'hiqus', target: {type: 'blogs', subtype: 'offtopic'}}

Suppose we called our script from the console by passing it the following parameters: list users sex = female limit = 100
After parsing we get: {'0': 'list', '1': 'users', 'sex': 'female', limit: '100'}

Let these cookies come to us: sid = 1234; login = tenshi
Get parsed and get: {sid: '1234', login: 'tenshi'}

Javascript reference implementation

var Hiqus= new function (){<br><br>Version: 1<br>Description: "parse, serialize and merge any 'HIerarhical QUery String'" <br>License: 'public domain' <br><br>Implementation:<br><br> var sepHiqusList= '/|; &' <br> var sepPathList= '=:_' <br><br> var sepHiqusDefault= sepHiqusList.charAt(0)<br> var sepPathDefault= sepPathList.charAt(0)<br><br> var sepHiqusRegexp= RegExp( '[' + sepHiqusList + ']' , 'g' )<br> var sepPathRegexp= RegExp( '[' + sepPathList + ']' , 'g' )<br><br> var encode= function ( str ){<br> return encodeURIComponent( str ).split( '_' ).join( '%5F' )<br>}<br> var decode= function ( str ){<br> return decodeURIComponent( str )<br>}<br><br> var splitHiqus= function ( str ){<br> return str.split( sepHiqusRegexp )<br>}<br> var splitPath= function ( str ){<br> var path= String( str ).split( sepPathRegexp )<br> for ( var j= path.length - 1; j >= 0; --j ) path[ j ]= decode( path[ j ] )<br> return path<br>}<br><br> var get= function ( ){<br> var obj= this <br> for ( var i= 0, len= arguments.length; i < len; ++i ){<br> var name= arguments[ i ]<br> if ( !name ) throw new Error( 'name is empty' )<br> obj= obj[ name ]<br> if ( typeof obj !== 'object' ) return obj<br> }<br> return obj<br>}<br><br> var placin= function ( ){<br> var obj= this <br> for ( var i= 0, len= arguments.length; i < len; ++i ){<br> var name= arguments[ i ]<br> if ( name && Number( name ) != name ){<br> var o= obj[ name ]<br> if (( !o )||( typeof o !== 'object' )) o= obj[ name ]= []<br> } else {<br> var o= []<br> obj.push( o )<br> }<br> obj= o<br> }<br> return obj<br>}<br><br> var put= function ( ){<br> var obj= this <br> var len= arguments.length<br> if ( !len ) return this <br> var val= arguments[ len - 1 ]<br> var key= arguments[ len - 2 ]<br> if ( len > 2 ){<br> var path= []<br> for ( var i= len - 2; i-- ;) path[ i ]= arguments[ i ]<br> obj= placin.apply( obj, path )<br> }<br> if ( typeof val === 'object' ){<br> var v= ( len === 1 ) ? obj : placin.call( obj, key )<br> for ( var i in val ) if ( val.hasOwnProperty( i ) ) put.call( v, i, val[ i ] )<br> } else {<br> val= String( val )<br> if ( !key || Number( key ) == key ) obj.push( val )<br> else obj[ key ]= val<br> }<br> return this <br>}<br><br> var parsin= function ( str ){ <br> var chunks= splitHiqus( str )<br> for ( var i= 0, len= chunks.length; i < len; ++i ){<br> var path= chunks[i]<br> if ( !path ) continue <br> path= splitPath( path )<br> put.apply( this , path )<br> }<br> return this <br>}<br><br> var serialize= function ( prefix, obj ){<br> if ( !obj ) return '' <br> if ( typeof obj !== 'object' ){<br> if ( prefix === sepPathDefault ) prefix= '' <br> return prefix+= encode( obj )<br> }<br> var list= []<br> for ( var key in obj ) if ( obj.hasOwnProperty( key ) ){<br> var k= ( Number( key ) == key ) ? '' : encode( key )<br> var chunk= serialize( prefix + k + sepPathDefault, obj[ key ] )<br> if ( chunk ) list.push( chunk )<br> }<br> return list.join( sepHiqusDefault )<br>}<br><br> var Hiqus= function ( ){<br> var hiqus= ( this instanceof Hiqus ) ? this : new Hiqus<br> var data= placin.call( hiqus, '_data' )<br> for ( var i= 0, len= arguments.length; i < len; ++i ){<br> var arg= arguments[i]<br> if ( arg instanceof Hiqus ) arg= arg._data<br> var invoke= ( Object( arg ) instanceof String ) ? parsin : put<br> invoke.call( data, arg )<br> }<br> return hiqus<br>}<br><br>Hiqus.prototype= new function (){<br><br> this .put= function (){<br> var hiqus= Hiqus( this )<br> put.apply( hiqus._data, arguments )<br> return hiqus<br> }<br> <br> this .get= function ( ){<br> var val= get.apply( this ._data, arguments )<br> if ( typeof val === 'object' ) val= put.call( [], val )<br> return val<br> }<br> <br> this .sub= function (){<br> var hiqus= Hiqus()<br> var val= get.apply( this ._data, arguments )<br> if ( typeof val !== 'object' ) val= [ val ]<br> hiqus._data= val<br> return hiqus<br> }<br><br> this .toString = function (){<br> var str= serialize( '' , this ._data )<br> this .toString= function (){ return str }<br> return str<br> }<br><br>}<br><br>Export: return Hiqus<br><br>Usage:<br><br>alert(<br> Hiqus<br> ( Hiqus( '| a:b:c:1 / a:b: ; a:b::c & a=b__d' )<br> .sub( 'a' , 'b' )<br> .put( [ 'e' , 'f' ] )<br> , 'g/h' <br> )<br> .get( 4 )<br>) // alerts 'g' <br><br>}

Hiqus objects implement the Immutable pattern, taking the necessary data through the constructor. It can have an arbitrary number of parameters of one of the following types:
  1. Line. It will be parsed and added to the tree.
  2. Hiqus object. His data will be attached to a tree.
  3. Json Similar to Hiqus object.
Methods of the hiqus object:
  1. put - returns a new object that is a copy of the original, but with a set value along a certain path. Hiqus (). Put ({user: {name: 'Nick'}}) is equivalent to Hiqus (). Put ('user', {name: 'Nick'}) and equivalent to Hiqus (). Put ('user', ' name ',' Nick ')
  2. get - returns a copy of the value in the specified path. Hiqus (). Get ('user', 'name') returns ambiguity, and Hiqus ('Nick; John'). Get () returns ['Nick', 'John']
  3. sub - creates a new object based on a value along a specific path. Hiqus ('users :: Nick | users :: John'). Sub ('users') is equivalent to Hiqus ('Nick; John')
  4. toString - converts to a string using a slash and an equal sign as delimiters. The result of the serialization is cached.

Tests

;( function ( x, y ){<br> if ( '' + x == y ) return arguments.callee<br> console.log( x, y )<br> throw new Error( 'fail test: [ ' + x + ' ]!=[ ' + y + ' ]' )<br>})<br><br>( Hiqus( ), '' )<br>( Hiqus( {} ), '' )<br>( Hiqus( [] ), '' )<br>( Hiqus( [1] ), '1' )<br>( Hiqus( { '' :1} ), '1' )<br>( Hiqus( { '' :{ '' :1}} ), '==1' )<br>( Hiqus( [1,2] ), '1/2' )<br>( Hiqus( {1:2} ), '2' )<br>( Hiqus( {a:1} ), 'a=1' )<br>( Hiqus( { 'a_b' :1} ), 'a%5Fb=1' )<br>( Hiqus( {a:{b:1}} ), 'a=b=1' )<br>( Hiqus( {a:1,b:2} ), 'a=1/b=2' )<br>( Hiqus( {a:1},{b:2} ), 'a=1/b=2' )<br>( Hiqus( {a:1}, 'b:2' ), 'a=1/b=2' )<br>( Hiqus( {a:1},{b:2} ), new Hiqus( {a:1},{b:2} ) )<br>( Hiqus( Hiqus({a:1}), Hiqus({b:2}) ), 'a=1/b=2' )<br><br>( Hiqus( '' ), '' )<br>( Hiqus( 'a' ), 'a' )<br>( Hiqus( '=a' ), 'a' )<br>( Hiqus( '==1' ), '==1' )<br>( Hiqus( 'a=1' ), 'a=1' )<br>( Hiqus( 'a:1' ), 'a=1' )<br>( Hiqus( 'a_1' ), 'a=1' )<br>( Hiqus( 'a=b=1' ), 'a=b=1' )<br>( Hiqus( 'a=1/b=2' ), 'a=1/b=2' )<br>( Hiqus( 'a=1;b=2' ), 'a=1/b=2' )<br>( Hiqus( 'a=1 b=2' ), 'a=1/b=2' )<br>( Hiqus( 'a=1&b=2' ), 'a=1/b=2' )<br>( Hiqus( 'a=1|b=2' ), 'a=1/b=2' )<br><br>( Hiqus( '1/2' ).get(), '1,2' )<br>( Hiqus( 'a=1/b=2' ).get( 'a' ), '1' )<br>( Hiqus( 'a=1=2/b=2=3' ).get( 'b' ,'0'), '3' )<br>( ( ( a= Hiqus( 'a=1/b=2' ) ).get( 'a' ), a ), 'a=1/b=2' )<br><br>( Hiqus( 'a=1/b=2' ).sub(), 'a=1/b=2' )<br>( Hiqus( 'a=b=1/a=c=2/d=3' ).sub( 'a' ), 'b=1/c=2' )<br>( Hiqus( 'a=b==1/a=b==2' ).sub( 'a' , 'b' ), '1/2' )<br>( ( a= Hiqus( 'a=1/b=2' ) ).sub( 'a' ) && a, 'a=1/b=2' )<br><br>( Hiqus( 'a=1' ).put( {b:2} ), 'a=1/b=2' )<br>( Hiqus( 'a=1' ).put( 'a' , {b:2} ), 'a=b=2' )<br>( ( a= Hiqus( 'a=1' ) ).put( 'b=2' ) && a, 'a=1' )<br>;

Source: https://habr.com/ru/post/92300/


All Articles